Everyone knows Monad but there is something also very useful and sometimes more powerful – Applicative Functors. I’m going to go from theory to real use cases where Applicative solved my issues.
10. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
11. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
12. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
13. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
14. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
15. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
16. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
17. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
18. Monad
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
20. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
21. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
22. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
23. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
24. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
25. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
26. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
27. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
28. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
29. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
30. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
31. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
32. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
33. Applicative
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
35. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
36. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
37. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
38. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
39. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
40. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
41. Applicative
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
43. Types
newtype Nickname = Nickname { getNickname :: String }
newtype Age = Age { getAge :: Int }
data User = User Nickname Age
> :t User
User :: Nickname -> Age -> User
44. As a function
readNickname :: Nickname
readAge :: Age
readUser :: User
readUser = User readNickname readAge
46. Using Monad
readNickname' :: IO Nickname
readAge' :: IO Age
readUserM :: IO User
readUserM = do
nickname <- readNickname'
age <- readAge'
return $ User nickname age
47. Using Applicative
readNickname' :: IO Nickname
readAge' :: IO Age
readUserA :: IO User
readUserA = pure User <*> readNickname' <*> readAge'
48. readNickname' :: IO Nickname
readAge' :: IO Age
readUserA :: IO User
readUserA = User <$> readNickname' <*> readAge'
Using Applicative
49. readNickname' :: IO Nickname
readAge' :: IO Age
readUserA :: IO User
readUserA = User <$> readNickname' <*> readAge'
Using Applicative
50. An infix synonym for 'fmap'
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap
51. Function vs Applicative
readNickname :: Nickname
readAge :: Age
User readNickname readAge
readNickname' :: IO Nickname
readAge' :: IO Age
User <$> readNickname' <*> readAge'
52. Applicative vs Monad
readNickname' :: IO Nickname
readNickname' = do
putStrLn "Your nickname:"
str <- getLine
if length str > 0
then return $ Nickname str
else error "Nickname can't be empty"
readAge' :: IO Age
readAge' = do
putStrLn "Your age:"
fmap (Age . read) getLine
53. Applicative vs Monad
readUserA :: IO User
readUserA = User <$> readNickname' <*> readAge'
readUserM :: IO User
readUserM = do
nickname <- readNickname'
age <- readAge'
return $ User nickname age
59. When we need
● Context-free validation/parsing/transformation/optimization/...
● Parallel computation
● Traverlable
● Easier to compose than Monads
● When we can’t / don’t want to create Monad because of extra laws
65. Example: Validation
validateNickname :: String -> Either [String] Nickname
validateNickname str
| size < 1 = Left [ "Nickname must have at least 1 character" ]
| size > 10 = Left [ "Nickname must have at most 10 characters" ]
| otherwise = Right $ Nickname str
where size = Data.List.length str
validateAge :: Int -> Either [String] Age
validateAge val
| val < 0 = Left [ "Minimal age is 0" ]
| val > 150 = Left [ "Maximal age is 150" ]
| otherwise = Right $ Age val
66. Example: Validation
validateUser :: String -> Int -> Either [String] User
validateUser nickname age =
User
<$> (validateNickname nickname)
<*> (validateAge age)
70. Example: Validation
> validateUser "" 200
Left ["Nickname must have at least 1 character"]
> validateUser "john" 200
Left ["Maximal age is 150"]
71. Example: Validation
> validateUser "" 200
Left ["Nickname must have at least 1 character"]
> validateUser "john" 200
Left ["Maximal age is 150"]
> validateUser "john" 20
Right (User (Nickname {getNickname = "john"}) (Age {getAge = 20}))
75. Example: Validation
● A data-type like Either but with an accumulating Applicative
● The Validation data type is isomorphic to Either
● An applicative functor that is not a monad
76. Example: Validation
● A data-type like Either but with an accumulating Applicative
● The Validation data type is isomorphic to Either
● An applicative functor that is not a monad
77. Example: Validation
validateNicknameA :: String -> Validation (NonEmpty String) Nickname
validateNicknameA str
| size < 1 = Failure $ "Nickname must have at least 1 character" :| []
| size > 10 = Failure $ "Nickname must have at most 10 characters" :| []
| otherwise = Success $ Nickname str
where size = Data.List.length str
validateAgeA :: Int -> Validation (NonEmpty String) Age
validateAgeA val
| val < 0 = Failure $ "Minimal age is 0" :| []
| val > 150 = Failure $ "Maximal age is 150" :| []
| otherwise = Success $ Age val
78. Example: Validation
validateUserA :: String -> Int -> Validation (NonEmpty String) User
validateUserA nickname age =
User
<$> (validateNicknameA nickname)
<*> (validateAgeA age)
81. Example: Validation
> validateUserA "" 200
Failure ("Nickname must have at least 1 character" :| ["Maximal age is 150"])
> validateUserA "john" 20
Success (User (Nickname {getNickname = "john"}) (Age {getAge = 20}))
82. Either vs Validation
instance Applicative (Either e) where
pure = Right
Left e <*> _ = Left e
Right f <*> r = fmap f r
instance Applicative (Validation err) where
pure = Success
Failure e1 <*> b = Failure $ case b of
Failure e2 -> e1 <> e2
Success _ -> e1
Success _ <*> Failure e2 =
Failure e2
Success f <*> Success a =
Success (f a)
83. Example: haxl
● batch multiple requests to the same data source
● request data from multiple data sources concurrently
● cache previous requests
● memoize computations
numCommonFriends x y =
length
<$> (intersect <$> friendsOf x <*> friendsOf y)
84. Example: Composition
readValidNickname :: IO (Maybe Nickname)
readValidNickname = do
putStrLn "Your name:"
str <- getLine
if length str > 0
then return $ Just $ Nickname str
else return Nothing
readValidAge :: IO (Maybe Age)
readValidAge = do
putStrLn "Your age:"
str <- getLine
return $ fmap Age $ readMaybe str
85. Example: Composition
readValidUserM :: IO (Maybe User)
readValidUserM = runMaybeT $ do
nickname <- MaybeT readValidNickname
age <- MaybeT readValidAge
return $ User nickname age
88. Example: Composition
newtype (:.:) (f :: k2 -> Type) (g :: k1 -> k2) (p :: k1) =
Comp1 { unComp1 :: f (g p) }
instance (Applicative f, Applicative g) => Applicative (f :.: g) where
pure x = Comp1 (pure (pure x))
Comp1 f <*> Comp1 x = Comp1 (liftA2 (<*>) f x)
liftA2 f (Comp1 x) (Comp1 y) = Comp1 (liftA2 (liftA2 f) x y)
89. Example: Free
data Field a =
StringField Name Doc
| IntField Name Doc
| BoolField Name Doc
| ObjectField Name Doc (Ap Field a)
90. Example: Free
type Dsl a = Ap Field a
string :: Name -> Doc -> Dsl String
string n d = liftAp $ StringField n d
int :: Name -> Doc -> Dsl Int
int n d = liftAp $ IntField n d
bool :: Name -> Doc -> Dsl Bool
bool n d = liftAp $ BoolField n d
object :: Name -> Doc -> Dsl a -> Dsl a
object n d a = liftAp $ ObjectField n d a
91. Example: Free
data User = User String Int Bool
data Comment = Comment String User
user :: Dsl User
user = User
<$> string "name" "user's name"
<*> int "age" "user's age"
<*> bool "active" "is active"
comment :: Dsl Comment
comment = Comment
<$> string "title" "short desc"
<*> object "creator" "who created the comment" user
93. Example: Free
genDoc :: Int -> Field a -> [String]
genDoc indent (StringField n d) = [printf "%*s- %s : string - %s" indent "" n d]
genDoc indent (IntField n d) = [printf "%*s- %s : int - %s" indent "" n d]
genDoc indent (BoolField n d) = [printf "%*s- %s : bool - %s" indent "" n d]
genDoc indent (ObjectField n d m) = asString : nested
where
asString = printf "%*s- %s : obj - %s" indent "" n d
nested = runAp_ (genDoc $ indent + 2) m
94. Example: Free
printDoc :: IO ()
printDoc = mapM_ putStrLn lines
where lines = runAp_ (genDoc 0) comment
- title : string - short desc
- creator : obj - creator of the comment
- name : string - user's name
- age : int - user's age
- active : bool - is active