forked from orion/obsidian
2.0 KiB
2.0 KiB
Generalizes Functor#map to Functions with 2+ arguments; lifting functions a -> b -> c
to f a -> f b -> f c
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 Tuples together its arguments
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:
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:
[(_ * 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
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 from Maybe fields; short-circuiting to Nothing
if any fields are Nothing
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'