This commit is contained in:
orion kindel 2024-09-22 14:24:51 -05:00
commit ebb7d92134
28 changed files with 957 additions and 0 deletions

21
fp/.obsidian/app.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
"mobileToolbarCommands": [
"command-palette:open",
"editor:insert-codeblock",
"editor:insert-wikilink",
"editor:insert-embed",
"editor:insert-tag",
"editor:attach-file",
"editor:toggle-bullet-list",
"editor:toggle-numbered-list",
"editor:toggle-checklist-status",
"editor:indent-list",
"editor:unindent-list",
"editor:undo",
"editor:redo",
"editor:configure-toolbar"
],
"livePreview": false,
"promptDelete": false,
"alwaysUpdateLinks": true
}

6
fp/.obsidian/appearance.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"theme": "obsidian",
"accentColor": "#745eff",
"interfaceFontFamily": "",
"baseFontSize": 18
}

3
fp/.obsidian/community-plugins.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
"templater-obsidian"
]

View File

@ -0,0 +1,30 @@
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"canvas": true,
"outgoing-link": true,
"tag-pane": true,
"properties": false,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"bookmarks": true,
"markdown-importer": false,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": false,
"sync": false
}

20
fp/.obsidian/core-plugins.json vendored Normal file
View File

@ -0,0 +1,20 @@
[
"file-explorer",
"global-search",
"switcher",
"graph",
"backlink",
"canvas",
"outgoing-link",
"tag-pane",
"page-preview",
"daily-notes",
"templates",
"note-composer",
"command-palette",
"editor-status",
"bookmarks",
"outline",
"word-count",
"file-recovery"
]

22
fp/.obsidian/graph.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"collapse-filter": true,
"search": "",
"showTags": false,
"showAttachments": false,
"hideUnresolved": false,
"showOrphans": true,
"collapse-color-groups": true,
"colorGroups": [],
"collapse-display": true,
"showArrow": false,
"textFadeMultiplier": 0,
"nodeSizeMultiplier": 1,
"lineSizeMultiplier": 1,
"collapse-forces": true,
"centerStrength": 0.518713248970312,
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1.9042482571987516,
"close": false
}

View File

@ -0,0 +1,30 @@
{
"command_timeout": 5,
"templates_folder": "Templates",
"templates_pairs": [
[
"",
""
]
],
"trigger_on_file_creation": false,
"auto_jump_to_cursor": false,
"enable_system_commands": true,
"shell_path": "",
"user_scripts_folder": "Scripts",
"enable_folder_templates": true,
"folder_templates": [
{
"folder": "",
"template": ""
}
],
"syntax_highlighting": true,
"syntax_highlighting_mobile": false,
"enabled_templates_hotkeys": [
""
],
"startup_templates": [
""
]
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
{
"id": "templater-obsidian",
"name": "Templater",
"version": "2.7.1",
"description": "Create and use templates",
"minAppVersion": "1.5.0",
"author": "SilentVoid",
"authorUrl": "https://github.com/SilentVoid13",
"helpUrl": "https://silentvoid13.github.io/Templater/",
"isDesktopOnly": false
}

View File

@ -0,0 +1,220 @@
.templater_search {
width: calc(100% - 20px);
}
.templater_div {
border-top: 1px solid var(--background-modifier-border);
}
.templater_div > .setting-item {
border-top: none !important;
align-self: center;
}
.templater_div > .setting-item > .setting-item-control {
justify-content: space-around;
padding: 0;
width: 100%;
}
.templater_div
> .setting-item
> .setting-item-control
> .setting-editor-extra-setting-button {
align-self: center;
}
.templater_donating {
margin: 10px;
}
.templater_title {
margin: 0;
padding: 0;
margin-top: 5px;
text-align: center;
}
.templater_template {
align-self: center;
margin-left: 5px;
margin-right: 5px;
width: 70%;
}
.templater_cmd {
margin-left: 5px;
margin-right: 5px;
font-size: 14px;
width: 100%;
}
.templater_div2 > .setting-item {
align-content: center;
justify-content: center;
}
.templater-prompt-div {
display: flex;
}
.templater-prompt-form {
display: flex;
flex-grow: 1;
}
.templater-prompt-input {
flex-grow: 1;
}
.templater-button-div {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 1rem;
}
textarea.templater-prompt-input {
height: 10rem;
}
textarea.templater-prompt-input:focus {
border-color: var(--interactive-accent);
}
.cm-s-obsidian .templater-command-bg {
left: 0px;
right: 0px;
background-color: var(--background-primary-alt);
}
.cm-s-obsidian .cm-templater-command {
font-size: 0.85em;
font-family: var(--font-monospace);
line-height: 1.3;
}
.cm-s-obsidian .templater-inline .cm-templater-command {
background-color: var(--background-primary-alt);
}
.cm-s-obsidian .cm-templater-command.cm-templater-opening-tag {
font-weight: bold;
}
.cm-s-obsidian .cm-templater-command.cm-templater-closing-tag {
font-weight: bold;
}
.cm-s-obsidian .cm-templater-command.cm-templater-interpolation-tag {
color: var(--code-property, #008bff);
}
.cm-s-obsidian .cm-templater-command.cm-templater-execution-tag {
color: var(--code-function, #c0d700);
}
.cm-s-obsidian .cm-templater-command.cm-keyword {
color: var(--code-keyword, #00a7aa);
font-weight: normal;
}
.cm-s-obsidian .cm-templater-command.cm-atom {
color: var(--code-normal, #f39b35);
}
.cm-s-obsidian .cm-templater-command.cm-value,
.cm-s-obsidian .cm-templater-command.cm-number,
.cm-s-obsidian .cm-templater-command.cm-type {
color: var(--code-value, #a06fca);
}
.cm-s-obsidian .cm-templater-command.cm-def,
.cm-s-obsidian .cm-templater-command.cm-type.cm-def {
color: var(--code-normal, var(--text-normal));
}
.cm-s-obsidian .cm-templater-command.cm-property,
.cm-s-obsidian .cm-templater-command.cm-property.cm-def,
.cm-s-obsidian .cm-templater-command.cm-attribute {
color: var(--code-function, #98e342);
}
.cm-s-obsidian .cm-templater-command.cm-variable,
.cm-s-obsidian .cm-templater-command.cm-variable-2,
.cm-s-obsidian .cm-templater-command.cm-variable-3,
.cm-s-obsidian .cm-templater-command.cm-meta {
color: var(--code-property, #d4d4d4);
}
.cm-s-obsidian .cm-templater-command.cm-callee,
.cm-s-obsidian .cm-templater-command.cm-operator,
.cm-s-obsidian .cm-templater-command.cm-qualifier,
.cm-s-obsidian .cm-templater-command.cm-builtin {
color: var(--code-operator, #fc4384);
}
.cm-s-obsidian .cm-templater-command.cm-tag {
color: var(--code-tag, #fc4384);
}
.cm-s-obsidian .cm-templater-command.cm-comment,
.cm-s-obsidian .cm-templater-command.cm-comment.cm-tag,
.cm-s-obsidian .cm-templater-command.cm-comment.cm-attribute {
color: var(--code-comment, #696d70);
}
.cm-s-obsidian .cm-templater-command.cm-string,
.cm-s-obsidian .cm-templater-command.cm-string-2 {
color: var(--code-string, #e6db74);
}
.cm-s-obsidian .cm-templater-command.cm-header,
.cm-s-obsidian .cm-templater-command.cm-hr {
color: var(--code-keyword, #da7dae);
}
.cm-s-obsidian .cm-templater-command.cm-link {
color: var(--code-normal, #696d70);
}
.cm-s-obsidian .cm-templater-command.cm-error {
border-bottom: 1px solid #c42412;
}
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 2px;
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
border-radius: 3px;
border: 1px solid silver;
background: white;
font-size: 90%;
font-family: monospace;
max-height: 20em;
overflow-y: auto;
}
.CodeMirror-hint {
margin: 0;
padding: 0 4px;
border-radius: 2px;
white-space: pre;
color: black;
cursor: pointer;
}
li.CodeMirror-hint-active {
background: #08f;
color: white;
}

164
fp/.obsidian/workspace-mobile.json vendored Normal file
View File

@ -0,0 +1,164 @@
{
"main": {
"id": "bccae7928f1a9905",
"type": "split",
"children": [
{
"id": "217d72f4193a5b9c",
"type": "tabs",
"children": [
{
"id": "8992473db8c2f73c",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Language/Infix Operators.md",
"mode": "source",
"source": true
}
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "b1878108e328d939",
"type": "mobile-drawer",
"children": [
{
"id": "33a47f685e8878d0",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical"
}
}
},
{
"id": "657f40de10459a91",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
}
}
},
{
"id": "e7a04a503b0bf0f1",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true
}
}
},
{
"id": "772471bd9f5963aa",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {}
}
}
],
"currentTab": 0
},
"right": {
"id": "c869efdcbafae729",
"type": "mobile-drawer",
"children": [
{
"id": "6899289773dd5346",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"file": "Language/Infix Operators.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
}
}
},
{
"id": "534b6ce9b4f07b6a",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "Language/Infix Operators.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
}
}
},
{
"id": "ff96767c96b7f595",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "Language/Infix Operators.md"
}
}
}
],
"currentTab": 0
},
"left-ribbon": {
"hiddenItems": {
"switcher:Open quick switcher": false,
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false,
"templater-obsidian:Templater": false
}
},
"active": "8992473db8c2f73c",
"lastOpenFiles": [
"Language/Infix Operators.md",
"Language/Functions.md",
"Language/Data Structures.md",
"Class/Functor.md",
"Monad/Effect",
"Monad/Aff",
"Language/Typeclasses.md",
"Language/Modules.md",
"Monad/Aff.md",
"Monad/Effect.md",
"Untitled",
"Monad",
"Data.md",
"Language",
"Data/Int.md",
"Class/Apply.md",
"Data/Array.md",
"Class",
"Data",
"Data/Maybe.md",
"Templates/Pursuit Link.md",
"Untitled.md",
"Templates",
"Templates",
"Untitled 1.md",
"Scripts/pursuit_link.js"
]
}

0
fp/.trash/Data.md Normal file
View File

0
fp/.trash/Untitled 1.md Normal file
View File

1
fp/.trash/Untitled.md Normal file
View File

@ -0,0 +1 @@
[Effect.untilE](https://pursuit.purescript.org/packages/purescript-effec

12
fp/Class/Apply.md Normal file
View File

@ -0,0 +1,12 @@
Generalizes [[Functor]] to functions with 2+ arguments
```haskell
apply ::
forall f a b
. Apply f
=> f (a -> b)
-> f a
-> f b
infixl apply as <*>
```

57
fp/Class/Functor.md Normal file
View File

@ -0,0 +1,57 @@
## What
[[Typeclasses|Typeclass]] defining the [[Functions|function]] `map`, [[Infix Operators|operators]] `<$>`, and `<#>`.
```haskell
class Functor f where
map :: forall a b. (a -> b) -> f a -> f b
```
## Why
Modify the data contained in a [[Data Structures|data structure]]
### Abstracts
- For every element in [[Collections|collection]] ...
- When [[Maybe|nullable]] value is non-null ...
- When _(potentially async)_ [[Effect|IO]] resolves ...
## Examples
### [[Array]] of [[Int]]
```haskell
add 1 <$> [1, 2, 3]
-- [2, 3, 4]
```
### [[Maybe]] of [[Int]]
```haskell
add 1 <$> Just 1
-- Just
add 1 <$> Nothing
-- Nothing
```
### [[Effect]] of [[Int]]
See also:
- [[Bind]]
- [[Show]]
- [[compose]]
- [[do notation]]
```haskell
import Node.FS.Sync (readTextFile, writeTextFile)
import Node.Encoding (Encoding(..))
import Data.Int as Int
writeNum :: Int -> Effect Unit
writeNum n = writeTextFile "num.txt" (show n) UTF8
readNum :: Effect Int
readNum =
readTextFile "num.txt" UTF8
>>= (Int.fromString >>> liftMaybe (error "invalid integer"))
main = do
writeNum
n <- add 1 <$> readNum
log $ show n
-- 2
```

2
fp/Data/Array.md Normal file
View File

@ -0,0 +1,2 @@
# What
# Why

5
fp/Data/Int.md Normal file
View File

@ -0,0 +1,5 @@
# What
Signed integers
# Why
You want numbers without decimals

2
fp/Data/Maybe.md Normal file
View File

@ -0,0 +1,2 @@
# What
# Why

View File

@ -0,0 +1,2 @@
# What
# Why

204
fp/Language/Functions.md Normal file
View File

@ -0,0 +1,204 @@
## Applying
Functions are applied by placing expressions after the function name, separated by whitespace, e.g.
```javascript
Math.pow(2, 10)
```
equivalent would be
```haskell
Number.pow 2.0 10.0
```
## Defining
Parts of a function definition are:
- type signature (optional)
- 1 or more "implementations" of the type signature
- name
- 0 or more arguments
- function body
### Type Signature
Function type signatures follow the form `<name> :: <type>`, and are always the line immediately above the function implementations, e.g.
this `add` has 2 `Int` arguments and returns an `Int`.
```haskell
add2 :: Int -> Int -> Int
-- ...
```
### Name
function names can contain any alphanumeric character, `_`, `'` but the first character must be a lowercase alpha or `_`.
### Arguments
Arguments are space-separated, e.g.
the first [[Int]] argument is bound to `a`, the second to `b`.
```haskell
add :: Int -> Int -> Int
add a b = -- ...
```
### Body
The expression returned by the function, e.g.
```haskell
add a b = a + b
```
### Multiple Implementations
Functions may have multiple implementations that change behavior based on the shape of the arguments.
This is _similar_ to method overloading in OOP languages, but differs in that the number of arguments and type of the arguments must be the same for all implementations.
Functions can have any number of implementations, as long as all possible inputs are covered exhaustively.
e.g.
this implementation of [[Number]] division has 2 paths:
- when the denominator is zero, yields `Infinity`
- otherwise, performs the division
```haskell
div num 0.0 = infinity
div num den = num / den
```
this is equivalent to
```haskell
div num den =
if den == 0.0 then
infinity
else
num / den
```
### Guards
Single function implementations may have guards, which allows for conditional checks on inputs (as opposed to structural pattern matching)
This pattern often takes up less space than explicit [[if then else Expressions]]
Guard patterns are placed after the function arguments and take the form `| <Boolean expr> = <body>`. There can be any number of guard patterns, as long as all cases are exhaustively covered.
e.g.
Strings can't be structurally pattern matched, so in order to ask if a string starts with a substring we need to call a function like `Data.String.Utils.startsWith`.
We could do this with `if then else`:
```haskell
ensureLeadingSlash :: String -> String
ensureLeadingSlash str =
if String.Util.startsWith "/" then
str
else
"/" <> str
```
Alternatively, we could implement this with guard patterns:
```haskell
ensureLeadingSlash :: String -> String
ensureLeadingSlash str
| String.Util.startsWith "/" str = str
| otherwise = "/" <> str
```
When the first pattern `String.Util.startsWith "/" str` returns `true`, the function will use `... = str`. Otherwise, it will prepend `/` to `str`.
> [!info]
> `otherwise` is simply an alias for `true`, specifically for better-reading fallthrough guard patterns.
## Currying
Functions are curried in PureScript, meaning that "a function of 2 arguments" is actually "a function of 1 argument, returning a function of 1 argument."
This allows you to call many functions point-free, and think in terms of "building up to a conclusion" rather than "i need everything at once."
e.g.
```haskell
add :: Int -> Int -> Int
-- ...
add2 :: Int -> Int
add2 n = add 2 n
```
is equivalent to
```haskell
add :: Int -> Int -> Int
-- ...
add2 :: Int -> Int
add2 = add 2
```
Walking through this:
`add` has type `Int -> Int -> Int`
if we give `add` a single `Int` argument, it will return a function of type `Int -> Int`. This function is the "second half" of `add`, waiting for it's second argument. Since `Int -> Int` is the type of `add2`, we can simply say `add2 = add 2`.
> [!info]
> as a rule, any time a function's last argument is passed as the last argument to another function, you can remove both.
> ```haskell
> f a = g b c a
>
> \a -> g b c a
>
> f a = g $ h a
> ```
> can be written as
> ```haskell
> f = g b c
>
> g b c
>
> f = g <<< h
> ```
## Working with Functions
Functions being first-class citizens means that it's important that applying, extending and combining functions must be easy.
### Composition
The most common pattern by far; piping the output of a function into the input of the next.
```haskell
compose :: forall a b c. (b -> c) -> (a -> b) -> (a -> c)
compose g f a = g (f a)
```
`compose` (infix as `<<<`) accepts 2 functions; `f` which is `a -> b` and `g`; `b -> c`. `compose` returns a new function `a -> c` that "glues" the 2 functions together; piping the output of `f` into `g`.
e.g.
consider a function `normalizedPathSegments :: String -> Array String`
This function would normalize a file path, removing trailing / leading slashes and resolving relative paths, then split the path by its segments.
A very good approach would be to split this function into separate single-purpose components, e.g.
- `stripLeadingSlash :: String -> String`
- `stripTrailingSlash :: String -> String`
- `splitPath :: String -> Array String`
- `normalizePathSegments :: Array String -> Array String`
then define `normalizedPathSegments` like so:
```haskell
normalizedPathSegments :: String -> Array String
normalizedPathSegments =
normalizePathSegments
<<< splitPath
<<< stripTrailingSlash
<<< stripLeadingSlash
```
map map map
(a -> b) -> (f a) -> (f b)
(a -> b) -> (c -> a) -> (c -> b)
((a -> b) -> (c -> a) -> (c -> b))
-> ((c -> b) -> (a -> b))
-> ((c -> b) -> (c -> a))

View File

@ -0,0 +1,107 @@
Infix operators are symbols that alias binary (2-argument) [[Functions|functions]].
They're defined like so:
```haskell
infixl <precedence> <fn> as <operator>
-- or
infixr -- ..
```
e.g.
```haskell
eq :: Int -> Int -> Boolean
eq = -- ...
add :: Int -> Int -> Int
add = -- ...
infixl 1 add as +
infixl 1 eq as ==
(1 + 2) == 3
-- same as
eq (add 1 2) 3
```
## Associativity
Operators are either left or right associative (`infixl` or `infixr`).
When multiple infix operators with the same precedence are chained, associativity tells the language how to group them, e.g. `&&` is right-associative, while `*` is left associative.
e.g.
```haskell
a && b && c
-- interpreted as
(a && (b && c))
a * b * c
-- interpreted as
((a * b) * c)
```
## Precedence
The precedence of operators is an int from 1-9 used as a tie-break, for example in
```haskell
a + b == c
```
this behaves how you'd expect; this is equivalent to
```haskell
((a + b) == c)
```
as the precedence of `+` (6) is higher than `==`'s (4), the grouping is first done around `a + b`.
## Common Operators
|Operator|Associativity|Precedence|Aliases|
|--|--|--|--|
|`$`||||
## Directionality
Most commonly used operators have flipped variants, e.g.
- function composition has `g <<< f` or `f >>> g`
- function application has `f $ a` or `a # f`
- [[Functor|map]] has `f <$> a` or `a <#> f`
- [[Bind|bind]] has `f =<< m` or `m >>= f`
In general, right-to-left operators tend to be easier to refactor into & out of because they closely mirror the expressions they replace:
```haskell
map (add 1) maybeNum
-- into
add 1 <$> maybeNum
foo (bar a)
-- into
foo <<< bar
```
Left-to-right, on the other hand, can read better to humans and plays better with pipelines containing both [[Bind|bind `>>=`]] and [[Functor|map `<#>`]].
Consider an expression piping `Maybe String` to `split :: String -> Array String` then `Data.Array.NonEmpty.fromArray :: Array a -> Maybe (NonEmptyArray a)`, then `toLower` each element:
```haskell
String.toLower
<$> (
Array.NonEmpty.fromArray
=<< String.split "."
<$> mStr
)
```
We need to wrap the right hand of the last `map` because the precedence of `=<<` is `1` (the lowest) and the precedence of `<$>` is `4`.
Written RTL, though, gives:
```haskell
mStr
<#> String.split "."
>>= Array.NonEmpty.fromArray
<#> String.toLower
```
This works because `<#>`'s precedence (1) is the same as `>>=`. The lower precedence on flipped map means you'll often need more parentheses wrapping its arguments `(..) <#> (..) >>= (..)` as opposed to entire expressions `.. <$> (.. =<< ..)`.
Personally, I try to stick to RTL except for expressions including bind.

0
fp/Language/Modules.md Normal file
View File

View File

@ -0,0 +1,2 @@
# What
# Why

2
fp/Monad/Aff.md Normal file
View File

@ -0,0 +1,2 @@
# What
# Why

2
fp/Monad/Effect.md Normal file
View File

@ -0,0 +1,2 @@
# What
# Why

View File

@ -0,0 +1,12 @@
module.exports = async (path) => {
path = await path
pkg = path.split('/')[0]
mod = path.split('/')[1]
let dots = path.split('.')
let mod = dots.filter(s => s[0].toUpperCase() === s[0]).join('.')
let last = dots[dots.length - 1]
let val = last[0].toLowerCase() === last[0] ? last : ''
val = val ? `#v:${val}` : ''
const url = `https://pursuit.purescript.org/packages/purescript-${pkg}/docs/${mod}${val}`
return `[${path}](${url})`
}

View File

@ -0,0 +1 @@
<% tp.user.pursuit_link(tp.system.prompt("Path", "", true)) %>