forked from orion/obsidian
79 lines
2.0 KiB
Markdown
79 lines
2.0 KiB
Markdown
|
Generalizes [[Functor#map|map]] to [[Functions|functions]] with 2+ arguments; lifting functions `a -> b -> c` to `f a -> f b -> f c`
|
||
|
|
||
|
```haskell
|
||
|
class Functor f <= Apply f where
|
||
|
apply :: forall a b. f (a -> b) -> f a -> f b
|
||
|
|
||
|
infixl apply as <*>
|
||
|
```
|
||
|
|
||
|
> [!tip]
|
||
|
> Apply often replaces the pattern of "building towards a final result"
|
||
|
>
|
||
|
> Consider a function that [[Tuple|tuple]]s together its arguments
|
||
|
> ```haskell
|
||
|
> mk3Tuple a b c = a /\ b /\ c
|
||
|
> ```
|
||
|
>
|
||
|
> A common situation is wanting to build something like this 3-tuple from some components that are trapped in a context like [[Maybe]] or [[Either]], and we want to say "when all these things are ok, build it"; this is exactly what Apply lets us do:
|
||
|
>
|
||
|
> ```haskell
|
||
|
> maybeMk3Tuple :: Maybe a -> Maybe b -> Maybe c -> Maybe (a /\ b /\ c)
|
||
|
> maybeMk3Tuple ma mb mc = Just mk3Tuple <*> ma <*> mb <*> mc
|
||
|
>
|
||
|
> maybeMk3Tuple (Just 1) (Just 2) (Just 3)
|
||
|
> -- Just (1 /\ 2 /\ 3)
|
||
|
>
|
||
|
> maybeMk3Tuple (Just "a") (Just 3) (Nothing)
|
||
|
> -- Nothing
|
||
|
>
|
||
|
> maybeMk3Tuple Nothing Nothing Nothing
|
||
|
> -- Nothing
|
||
|
> ```
|
||
|
|
||
|
> [!tip]
|
||
|
> Obscure but occasionally usefully, Apply in collections lets us apply multiple functions to each element:
|
||
|
> ```haskell
|
||
|
> [(_ * 10), (_ * 20)] <*> [1, 2, 3]
|
||
|
> -- [10, 20, 20, 40, 30, 60]
|
||
|
>
|
||
|
> [identity, const ", "] <*> ["a", "b", "c"]
|
||
|
> -- ["a", ",", "b", ",", "c", ","]
|
||
|
> ```
|
||
|
|
||
|
### Examples
|
||
|
|
||
|
lift `(a + b) / c` to [[Maybe]]
|
||
|
```haskell
|
||
|
f :: Number -> Number -> Number
|
||
|
f a b c = (a + b) / c
|
||
|
|
||
|
mf :: Maybe Number -> Maybe Number -> Maybe Number
|
||
|
mf ma mb mc = pure f <*> ma <*> mb <*> mc
|
||
|
```
|
||
|
|
||
|
Build up a `Person` [[Record|record]] from Maybe fields; short-circuiting to `Nothing` if any fields are `Nothing`
|
||
|
```haskell
|
||
|
type PartialPerson =
|
||
|
{ name :: Maybe String
|
||
|
, email :: Maybe String
|
||
|
, password :: Maybe String
|
||
|
}
|
||
|
|
||
|
type Person =
|
||
|
{ name :: String
|
||
|
, email :: String
|
||
|
, password :: String
|
||
|
}
|
||
|
|
||
|
person :: PartialPerson -> Person
|
||
|
person
|
||
|
{ name: name'
|
||
|
, email: email'
|
||
|
, password: password'
|
||
|
} =
|
||
|
let
|
||
|
buildPerson name email password = {name, email, password}
|
||
|
in
|
||
|
Just buildPerson <*> name' <*> email' <*> password'
|
||
|
```
|