Applicative
| Functions in a context | |
| pure | |
| Validation with lists | |
| liftA2 and friends |
Functions in a context
fmap applies a plain function to a wrapped value. Applicative goes one step further and applies a wrapped function:
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Just (+1) <*> Just 2 -- Just 3
[(*2), (+10)] <*> [1,2] -- [2,4,11,12]
The list case applies every function to every value, which is the cartesian product. That looks odd until you use it for validation in the next section.
pure
pure puts a plain value into the smallest possible context. For Maybe that is Just, for a list it is a one-element list:
pure 5 :: Maybe Int -- Just 5
pure 5 :: [Int] -- [5]
pure (+) <*> Just 1 <*> Just 2 -- Just 3
The second line is the idiomatic way to combine two wrapped values with a plain function. Once you see it, liftA2 in the last section is just a name for the same idea.
Validation with lists
A list applicative collects every failure instead of stopping at the first. This is how a form reports three bad fields at once:
check :: Int -> [String]
check n
| n < 0 = ["too small"]
| n > 100 = ["too big"]
| otherwise = []
validate a b = (\_ _ -> ()) <$> check a <*> check b
-- validate (-1) 200 == ["too small","too big"]
The same shape with Maybe stops at the first Nothing, because a Maybe has at most one failure to report. The container decides the strategy, not the calling code.
liftA2 and friends
The library names the common arities so that the operator soup disappears. All of them are defined with pure and <*>:
liftA2 (+) (Just 1) (Just 2) -- Just 3
liftA2 (+) Nothing (Just 2) -- Nothing
sequenceA [Just 1, Just 2] -- Just [1,2]
sequenceA [Just 1, Nothing] -- Nothing
sequenceA turns a list of containers into a
container of a list, failing if any element fails. Lesson 44
generalises it to Traversable.
Next:
Monad
Article author: Arthur Isaev