forked from orion/obsidian
update
This commit is contained in:
parent
d74fc46755
commit
3ee2a9bbbd
50
fp/.obsidian/workspace.json
vendored
50
fp/.obsidian/workspace.json
vendored
@ -11,8 +11,12 @@
|
||||
"id": "6465d16034124b8f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "graph",
|
||||
"state": {}
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Language/Functions/Defining/Guard Clause.md",
|
||||
"mode": "source",
|
||||
"source": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -81,6 +85,7 @@
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "Language/Functions/Defining/Guard Clause.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
@ -97,6 +102,7 @@
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "Language/Functions/Defining/Guard Clause.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
@ -118,7 +124,9 @@
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {}
|
||||
"state": {
|
||||
"file": "Language/Functions/Defining/Guard Clause.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -142,6 +150,24 @@
|
||||
},
|
||||
"active": "6465d16034124b8f",
|
||||
"lastOpenFiles": [
|
||||
"Language/Functions/Defining/Pattern Matching.md",
|
||||
"Language/Functions/Applying.md",
|
||||
"Language/Functions/Composition.md",
|
||||
"Language/Functions/Currying.md",
|
||||
"Language/Functions.md",
|
||||
"Terminology/Point-free.md",
|
||||
"Language/Infix Operators/Common Operators/Applying & Composing Functions.md",
|
||||
"Language/Functions/Defining.md",
|
||||
"Language/Expressions/case .. of.md",
|
||||
"Language/Expressions/Lambda Functions.md",
|
||||
"Language/Functions/Defining/Guard Clause.md",
|
||||
"Language/Expressions/let .. in ...md",
|
||||
"Language/Expressions/if .. then .. else ...md",
|
||||
"Language/Expressions/do notation.md",
|
||||
"Classes/Functor/Apply.md",
|
||||
"Classes/Functor/Applicative.md",
|
||||
"Classes/Bind/Monad.md",
|
||||
"Classes/Bind/Bind.md",
|
||||
"Classes/Alternative/Alt.md",
|
||||
"Classes/Alternative/Plus.md",
|
||||
"Monads/Transformers/MaybeT.md",
|
||||
@ -151,31 +177,13 @@
|
||||
"Monads/Transformers/StateT.md",
|
||||
"Monads/Transformers",
|
||||
"Classes/Alternative/Alternative.md",
|
||||
"Classes/Functor/Applicative.md",
|
||||
"Classes/Functor/Apply.md",
|
||||
"Classes/Functor/Functor.md",
|
||||
"Classes/Collapsing",
|
||||
"Classes/Bind",
|
||||
"Classes/Functor",
|
||||
"Classes/Alternative",
|
||||
"Language/Infix Operators/Common Operators/Data.md",
|
||||
"Classes/Traversable.md",
|
||||
"Data/Collections/NonEmptyArray.md",
|
||||
"Data/Collections/NonEmptyList.md",
|
||||
"Data/String.md",
|
||||
"Data/Collections.md",
|
||||
"Data/Collections/HashMap.md",
|
||||
"Data/Product",
|
||||
"Data/Sum",
|
||||
"Data/Collections/HashSet.md",
|
||||
"Data/Collections/Set.md",
|
||||
"Data/Collections/List.md",
|
||||
"Data/Collections/Map.md",
|
||||
"Data/Collections",
|
||||
"Language/Row Types.md",
|
||||
"Language/Row Types/Variant.md",
|
||||
"Language/Row Types/Record.md",
|
||||
"Data/Product/Tuple.md",
|
||||
"Language/Row Types",
|
||||
"Classes/Math",
|
||||
"Untitled 1.canvas",
|
||||
|
23
fp/Language/Expressions/Lambda Functions.md
Normal file
23
fp/Language/Expressions/Lambda Functions.md
Normal file
@ -0,0 +1,23 @@
|
||||
lambda functions are anonymous functions (as opposed to [[Functions|top-level functions]]) that can be written anywhere:
|
||||
|
||||
```haskell
|
||||
\ <params> -> <body>
|
||||
```
|
||||
|
||||
```haskell
|
||||
prependHello a = "hello, " <> a
|
||||
|
||||
appendName = \a -> a <> "henry!"
|
||||
|
||||
str = appendName $ prependHello ""
|
||||
-- hello, henry!
|
||||
```
|
||||
|
||||
their parameters can destructure their arguments:
|
||||
```haskell
|
||||
let
|
||||
getFoo = \{foo} -> foo
|
||||
in
|
||||
getFoo {foo: "hello"}
|
||||
-- "hello"
|
||||
```
|
@ -1,4 +1,4 @@
|
||||
Pattern matching
|
||||
Structural pattern matching expression
|
||||
|
||||
```haskell
|
||||
case <expr> of
|
||||
@ -18,7 +18,7 @@ where `arm` is:
|
||||
- array literals, containing patterns
|
||||
- wildcard `_`
|
||||
|
||||
### Guard Clauses
|
||||
## Guard Clauses
|
||||
Case arms can have [[Functions/Defining/Guard Clause|guard clauses]] like [[Functions|function]] definitions:
|
||||
|
||||
```haskell
|
||||
@ -28,7 +28,22 @@ case _ of
|
||||
| otherwise -> Nothing
|
||||
```
|
||||
|
||||
### Examples
|
||||
## Anonymous expression
|
||||
Underscore can be placed in the expression position to write an [[Lambda Functions|anonymous function]] that immediately case matches on its argument:
|
||||
|
||||
```haskell
|
||||
case _ of
|
||||
Foo -> true
|
||||
Bar -> false
|
||||
|
||||
-- equivalent to
|
||||
|
||||
\a -> case a of
|
||||
Foo -> true
|
||||
Bar -> false
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Match on `Int` when zero
|
||||
```haskell
|
||||
|
@ -0,0 +1,209 @@
|
||||
Do notation is sugar for [[Bind|bind]] `>>=`, allowing imperative blocks that use bind as the driver of the "and-then" behavior for the specific type being returned.
|
||||
|
||||
```haskell
|
||||
main :: Effect Unit
|
||||
main = do
|
||||
log "hello, world!"
|
||||
log "foo bar baz"
|
||||
|
||||
-- logs:
|
||||
-- hello, world!
|
||||
-- foo bar baz
|
||||
```
|
||||
|
||||
Do blocks are opened with the `do` keyword and support only a few operations:
|
||||
|
||||
- [[#bind|"await" with <-]]
|
||||
- [[#let-bindings|define local variables]]
|
||||
- [[#eval and ignore|evaluate an expression but discard its output]]
|
||||
- [[#return|return an expression]]
|
||||
|
||||
statements in do blocks are evaluated line-by-line top-to-bottom, just like any other imperative language.
|
||||
|
||||
## bind
|
||||
The left-arrow `<-` in `do` blocks is analogous to the `await` keyword in JS, C# & Java, and the question mark operator in Rust.
|
||||
|
||||
```haskell
|
||||
intPositive :: Int -> Maybe Int
|
||||
intPositive n
|
||||
| n > 0 = Just n
|
||||
| otherwise = Nothing
|
||||
|
||||
f :: String -> Maybe Int
|
||||
f s = do
|
||||
-- if `s` can't be parsed, returns Nothing early
|
||||
num <- Int.fromString s
|
||||
-- if `num` is <= 0, returns Nothing early
|
||||
pos <- intPositive num
|
||||
Just pos
|
||||
```
|
||||
|
||||
This desugars to:
|
||||
```haskell
|
||||
f s =
|
||||
Int.fromString s
|
||||
>>= ( \num ->
|
||||
intPositive num
|
||||
>>= (\pos -> Just pos)
|
||||
)
|
||||
```
|
||||
|
||||
## let-bindings
|
||||
Related to [[let .. in ..]], You can define new local variables in the middle of do blocks that can depend on previous bindings:
|
||||
|
||||
```haskell
|
||||
do
|
||||
a <- Just 1
|
||||
let
|
||||
b = a + 1
|
||||
c = b + 1
|
||||
Just c
|
||||
-- evaluates to (Just 3)
|
||||
```
|
||||
|
||||
> [!attention]- Unexpected or mismatched indentation
|
||||
> A common gotcha with let-bindings in do blocks is only indenting once after trying to insert a line break after the equals:
|
||||
> ```haskell
|
||||
> do
|
||||
> a <- Just 1
|
||||
> let b =
|
||||
> a + 1
|
||||
> Just b
|
||||
> ```
|
||||
>
|
||||
> ```text
|
||||
> [ERROR 1/1 ErrorParsingModule] ...
|
||||
>
|
||||
> 4 a + 1
|
||||
>
|
||||
>
|
||||
> Unable to parse module:
|
||||
> Unexpected or mismatched indentation
|
||||
> ```
|
||||
>
|
||||
> This happens because oneline let bindings
|
||||
> ```haskell
|
||||
> do
|
||||
> let a = b
|
||||
> ..
|
||||
> ```
|
||||
> are identical to
|
||||
> ```haskell
|
||||
> do
|
||||
> let
|
||||
> a = b
|
||||
> ..
|
||||
> ```
|
||||
> So when you break after the equals and only indent once:
|
||||
> ```haskell
|
||||
> do
|
||||
> let a =
|
||||
> b
|
||||
> ..
|
||||
> ```
|
||||
> what you're actually writing is
|
||||
> ```haskell
|
||||
> do
|
||||
> let
|
||||
> a =
|
||||
> b
|
||||
> ..
|
||||
> ```
|
||||
|
||||
## eval and ignore
|
||||
When an expression returns `m Unit`, you don't have to write an arrow:
|
||||
```haskell
|
||||
guard :: Boolean -> Maybe Unit
|
||||
guard true = Just unit
|
||||
guard false = Nothing
|
||||
|
||||
do
|
||||
n <- Int.fromString "123"
|
||||
-- when `n > 0`, this resolves to `Just unit` and
|
||||
-- the execution continues.
|
||||
-- If this is Nothing, the block short-circuits
|
||||
-- and returns Nothing.
|
||||
guard (n > 0)
|
||||
Just n
|
||||
```
|
||||
|
||||
If you want to discard a value other than unit, you need to use the discard syntax `_ <- x`
|
||||
|
||||
```haskell
|
||||
main :: Effect Unit
|
||||
main = do
|
||||
fooTxt <- File.createWriteStream "foo.txt"
|
||||
-- Stream.writeString returns a Boolean
|
||||
-- indicating whether the stream needs us
|
||||
-- to wait for the `drain` event before writing
|
||||
-- more, but we don't care here.
|
||||
_ <- Stream.writeString UTF8 "foo" fooTxt
|
||||
pure unit
|
||||
```
|
||||
|
||||
## return
|
||||
the last statement in a do block is the expression the do block will return, and can't be a let-binding or bind statement:
|
||||
|
||||
```haskell
|
||||
-- block immediately resolves with `Just ""`
|
||||
do
|
||||
Just ""
|
||||
```
|
||||
|
||||
```haskell
|
||||
-- block resolves with `Just foo`
|
||||
do
|
||||
foo <- getFoo
|
||||
Just foo
|
||||
```
|
||||
|
||||
```haskell
|
||||
-- block resolves with `Just "foo"`
|
||||
do
|
||||
do
|
||||
do
|
||||
do
|
||||
Just "foo"
|
||||
```
|
||||
|
||||
## Examples
|
||||
`do` in [[Maybe]] allows us to return early with empty values.
|
||||
|
||||
In this example, `getApiUrlPort` will return Nothing if:
|
||||
- environment variable `API_URL` is unset
|
||||
- `API_URL` is not a valid URL
|
||||
- `API_URL` is a valid URL, but the port isn't set
|
||||
|
||||
```haskell
|
||||
lookupEnv :: String -> Maybe String
|
||||
urlFromString :: String -> Maybe URL
|
||||
urlPort :: URL -> Maybe Int
|
||||
|
||||
getApiUrlPort = do
|
||||
apiUrlString <- lookupEnv "API_URL"
|
||||
apiUrl <- urlFromString apiUrlString
|
||||
urlPort apiUrl
|
||||
```
|
||||
|
||||
`do` in [[Either]] allows us to return early with errors.
|
||||
|
||||
```haskell
|
||||
lookupEnv :: String -> Either String String
|
||||
urlFromString :: String -> Either String URL
|
||||
urlPort :: URL -> Maybe Int
|
||||
|
||||
getApiUrlPort = do
|
||||
apiUrlString <- lookupEnv "API_URL"
|
||||
apiUrl <- urlFromString apiUrlString
|
||||
liftMaybe ("no port set in URL " <> apiUrlString)
|
||||
$ urlPort apiUrl
|
||||
```
|
||||
|
||||
> [!tip]- `liftMaybe`
|
||||
> liftMaybe allows us to turn `Maybe a` into any `m a` as long as `m` supports [[MonadThrow|throwing]] errors.
|
||||
>
|
||||
> In the above example, if `urlPort` is `Nothing`, it will return `Left "no port set in ..."`.
|
||||
>
|
||||
> ```haskell
|
||||
> liftMaybe :: forall m e a. MonadThrow e m => e -> Maybe a => m a
|
||||
> ```
|
@ -0,0 +1,5 @@
|
||||
Conditional expression, similar to ternary expressions in imperative languages:
|
||||
|
||||
```haskell
|
||||
if a == b then "foo" else "bar"
|
||||
```
|
@ -0,0 +1,33 @@
|
||||
`let .. in ..` is an expression with some scoped variables, separated by newlines:
|
||||
|
||||
```haskell
|
||||
let
|
||||
a = 1
|
||||
b = 2
|
||||
in
|
||||
a + b
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
> there is a second local let-binding syntax, `where`:
|
||||
> ```haskell
|
||||
> a + b
|
||||
> where
|
||||
> a = 1
|
||||
> b = 2
|
||||
> ```
|
||||
|
||||
### Examples
|
||||
|
||||
Recursive Fibonacci generator, using a helper function `go`
|
||||
```haskell
|
||||
fib n =
|
||||
let
|
||||
go ns a b =
|
||||
if Array.length ns == n then
|
||||
ns
|
||||
else
|
||||
go (ns <> [a]) b (a + b)
|
||||
in
|
||||
go [] 1 1
|
||||
```
|
@ -1 +1,3 @@
|
||||
The backbone of functional programming. Functions can be constants, transform [[Data Structures|data]], perform [[Effect|effects]] and much more.
|
||||
The backbone of functional programming. Functions can be constants, transform [[Data Structures|data]], perform [[Effect|effects]] and much more.
|
||||
|
||||
Functions can be [[Defining|defined]] as top-level [[Modules|module]] items, [[Lambda Functions|lambda functions]], [[Infix Operators|infix operators]] or created by [[Currying|partially applying]] functions.
|
@ -1,6 +1,6 @@
|
||||
[[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."
|
||||
This allows you to call many functions [[Point-free|point-free]], and think in terms of "building up to a conclusion" rather than "i need everything at once."
|
||||
|
||||
e.g.
|
||||
|
||||
@ -27,7 +27,7 @@ Walking through this:
|
||||
|
||||
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]
|
||||
> [!tip]
|
||||
> 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
|
||||
|
@ -1,14 +1,14 @@
|
||||
Single [[Functions|Functions]] [[Defining/Pattern Matching|implementations]] may have 1 or more guard clauses.
|
||||
|
||||
> [!info]
|
||||
> [!tip]
|
||||
> [[case .. of]] expressions can also use guard clauses.
|
||||
|
||||
These allow functions to switch on boolean expressions based on the inputs, and can often take up less space than explicit [[Expressions/if .. then .. else ..]] expressions.
|
||||
These allow functions to switch on boolean expressions based on the inputs, and can often take 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.
|
||||
|
||||
### Example
|
||||
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`.
|
||||
[[String]]s can't be structurally pattern matched aside from the entire string. 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`:
|
||||
|
||||
@ -31,7 +31,7 @@ ensureLeadingSlash str
|
||||
|
||||
When the first pattern `String.Util.startsWith "/" str` returns `true`, the function will use `... = str`. Otherwise, it will prepend `/` to `str`.
|
||||
|
||||
> [!tip]
|
||||
> [!tip] `otherwise`
|
||||
> `otherwise` is simply an alias for `true`, specifically for better-reading fallthrough guard patterns.
|
||||
|
||||
Much less often, but occasionally useful is the ability to structurally pattern match in a guard clause:
|
||||
|
@ -1,7 +1,17 @@
|
||||
[[Functions]] may have multiple implementations that change behavior based on the shape of the arguments.
|
||||
|
||||
> [!note]
|
||||
> You can always replace this with a [[case .. of|case expression]].
|
||||
> [!tip]- [[case .. of]]
|
||||
> Pattern matching in function definitions can always be rewritten as [[case .. of]].
|
||||
> ```haskell
|
||||
> isA "a" = true
|
||||
> isA _ = false
|
||||
> ```
|
||||
> can be rewritten as
|
||||
> ```haskell
|
||||
> isA a = case a of
|
||||
> "a" -> true
|
||||
> _ -> false
|
||||
> ```
|
||||
|
||||
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.
|
||||
|
||||
|
18
fp/Terminology/Point-free.md
Normal file
18
fp/Terminology/Point-free.md
Normal file
@ -0,0 +1,18 @@
|
||||
Written without explicit arguments and application, e.g.
|
||||
```haskell
|
||||
foo a b = bar a b
|
||||
```
|
||||
can be written point-free as
|
||||
```haskell
|
||||
foo = bar
|
||||
```
|
||||
|
||||
Rewriting
|
||||
```haskell
|
||||
\a -> g (f a)
|
||||
```
|
||||
as
|
||||
```haskell
|
||||
g << f
|
||||
```
|
||||
is also referred to as point-free.
|
Loading…
Reference in New Issue
Block a user