init
This commit is contained in:
commit
ebb7d92134
21
fp/.obsidian/app.json
vendored
Normal file
21
fp/.obsidian/app.json
vendored
Normal 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
6
fp/.obsidian/appearance.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"theme": "obsidian",
|
||||
"accentColor": "#745eff",
|
||||
"interfaceFontFamily": "",
|
||||
"baseFontSize": 18
|
||||
}
|
3
fp/.obsidian/community-plugins.json
vendored
Normal file
3
fp/.obsidian/community-plugins.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
"templater-obsidian"
|
||||
]
|
30
fp/.obsidian/core-plugins-migration.json
vendored
Normal file
30
fp/.obsidian/core-plugins-migration.json
vendored
Normal 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
20
fp/.obsidian/core-plugins.json
vendored
Normal 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
22
fp/.obsidian/graph.json
vendored
Normal 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
|
||||
}
|
30
fp/.obsidian/plugins/templater-obsidian/data.json
vendored
Normal file
30
fp/.obsidian/plugins/templater-obsidian/data.json
vendored
Normal 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": [
|
||||
""
|
||||
]
|
||||
}
|
19
fp/.obsidian/plugins/templater-obsidian/main.js
vendored
Normal file
19
fp/.obsidian/plugins/templater-obsidian/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
fp/.obsidian/plugins/templater-obsidian/manifest.json
vendored
Normal file
11
fp/.obsidian/plugins/templater-obsidian/manifest.json
vendored
Normal 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
|
||||
}
|
220
fp/.obsidian/plugins/templater-obsidian/styles.css
vendored
Normal file
220
fp/.obsidian/plugins/templater-obsidian/styles.css
vendored
Normal 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
164
fp/.obsidian/workspace-mobile.json
vendored
Normal 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
0
fp/.trash/Data.md
Normal file
0
fp/.trash/Untitled 1.md
Normal file
0
fp/.trash/Untitled 1.md
Normal file
1
fp/.trash/Untitled.md
Normal file
1
fp/.trash/Untitled.md
Normal file
@ -0,0 +1 @@
|
||||
[Effect.untilE](https://pursuit.purescript.org/packages/purescript-effec
|
12
fp/Class/Apply.md
Normal file
12
fp/Class/Apply.md
Normal 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
57
fp/Class/Functor.md
Normal 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
2
fp/Data/Array.md
Normal file
@ -0,0 +1,2 @@
|
||||
# What
|
||||
# Why
|
5
fp/Data/Int.md
Normal file
5
fp/Data/Int.md
Normal file
@ -0,0 +1,5 @@
|
||||
# What
|
||||
Signed integers
|
||||
|
||||
# Why
|
||||
You want numbers without decimals
|
2
fp/Data/Maybe.md
Normal file
2
fp/Data/Maybe.md
Normal file
@ -0,0 +1,2 @@
|
||||
# What
|
||||
# Why
|
2
fp/Language/Data Structures.md
Normal file
2
fp/Language/Data Structures.md
Normal file
@ -0,0 +1,2 @@
|
||||
# What
|
||||
# Why
|
204
fp/Language/Functions.md
Normal file
204
fp/Language/Functions.md
Normal 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))
|
||||
|
107
fp/Language/Infix Operators.md
Normal file
107
fp/Language/Infix Operators.md
Normal 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
0
fp/Language/Modules.md
Normal file
2
fp/Language/Typeclasses.md
Normal file
2
fp/Language/Typeclasses.md
Normal file
@ -0,0 +1,2 @@
|
||||
# What
|
||||
# Why
|
2
fp/Monad/Aff.md
Normal file
2
fp/Monad/Aff.md
Normal file
@ -0,0 +1,2 @@
|
||||
# What
|
||||
# Why
|
2
fp/Monad/Effect.md
Normal file
2
fp/Monad/Effect.md
Normal file
@ -0,0 +1,2 @@
|
||||
# What
|
||||
# Why
|
12
fp/Scripts/pursuit_link.js
Normal file
12
fp/Scripts/pursuit_link.js
Normal 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})`
|
||||
}
|
1
fp/Templates/Pursuit Link.md
Normal file
1
fp/Templates/Pursuit Link.md
Normal file
@ -0,0 +1 @@
|
||||
<% tp.user.pursuit_link(tp.system.prompt("Path", "", true)) %>
|
Loading…
Reference in New Issue
Block a user