haskell - Functors, Applicative Functors and Monoids - Applicatives
Applicative is a special form of functor, in our previoius post we have already discussed the functor with ((->) r);
now, we will see the beefed up functor, which is in the Applicator typeclass,and you will import the Control.Applicative module.
and we will first examine how we can apply partially applied function to the Appilcative functors. so first let's see the use example of the functor as such
ghci> :t fmap (++) (Just "hey") fmap (++) (Just "hey") :: Maybe ([Char] -> [Char]) ghci> :t fmap compare (Just 'a') fmap compare (Just 'a') :: Maybe (Char -> Ordering) ghci> :t fmap compare "A LIST OF CHARS" fmap compare "A LIST OF CHARS" :: [Char -> Ordering] ghci> :t fmap (\x y z -> x + y / z) [3,4,5,6] fmap (\x y z -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]
so, what if we apply the functor mapping on the "multi-parameter" functions over functors?? first let 's see the example as below;.
ghci> let a = fmap (*) [1,2,3,4] ghci> :t a a :: [Integer -> Integer] ghci> fmap (\f -> f 9) a [9,18,27,36]
as we can see, when map (*) on the [1,2,3,4] (you can treat the [1,2,3,4] as the [] functor on the nmbers of 1,2,3,4 respectively); then you will get back a list of functors, and then you apply the function \f ->f to each of them.
this is how the Applicative typeclass is defined, it is defined in the Control.Applicative module.
class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f bto be an Applicative, you first has to be a Functor,and this has been manifested by the type constraint. the pure function servers as the "applicative functor instance" it means take value of any type and return an applicative functor with that value in it.
the interesting part of the Applicative functor is the <*> function. the type of the functor is f (a -> b) -> f a -> f b ;it reminds us that it resembles that of (a -> b) -> f a -> f b, which is the type of fmap function. so it means it can take a function and a funcotr and apply the function inside the functor... It basically performs a "do and run"...
so, if we were to implement the Applicative functor on Maybe type. here it is.
instance Applicative Maybe where pure = Just Nothing <*> _ = Nothing (Just f) <*> something = fmap f something
the reasoning of the Just is ignored. Let's see how we can apply that Just to the Maybe type.
ghci> Just (+3) <*> Just 9 Just 12 ghci> pure (+3) <*> Just 10 Just 13 ghci> pure (+3) <*> Just 9 Just 12 ghci> Just (++"hahah") <*> Nothing Nothing ghci> Nothing <*> Just "woot" Nothing
With normal functors, you can just map a function over a functor and then you can't get the result out in any general way, even if the result is a partially applied function. Applicative functors, on the other hand, allow you to operate on several functors with a single function. Check out this piece of code:
ghci> pure (+) <*> Just 3 <*> Just 5 Just 8 ghci> pure (+) <*> Just 3 <*> Nothing Nothing ghci> pure (+) <*> Nothing <*> Just 5 Nothing
So, you have observed, the Applicative function gives that ability to chain something together. so as in "ability to operte on several functors with a single function.", <*> is left associative.
This can be even handy and apparent if we use the <$> notation, which has the folowing signatjure.
(<$>) :: (Functor f) => (a -> b) -> f a -> f b f <$> x = fmap f xit just takes the (fmap) function as the infix operator,it does a lifting, so that instead of writing pure f <*> x <*> y <*> you can write fmap f x <*> y <*> ...and which can be even simplified as f <$> x <*> y <*> ...
here is the some examples that leveerage the <$> notation and other to performs some transformation.
ghci> (++) <$> Just "johntra" <*> Just "volta" Just "johntravolta"
so, in a nutshell, the applicative function will take a applicative and returns an applicative. Hoiw cool is that..
Some times,you nee dto instruct to the just function to let it know the types. see below.
ghci> pure "Hey" :: [String] ["Hey"] ghci> pure "Hey" :: Maybe String Just "Hey"Let's take another look of the <*> on list ony. it should have a type of (<*>) :: [a -> b] -> [a] -> [b]. , and it is implemented as List comporehension. and what we will get is a list comprehension behavior, where every possible function from the left list to every possible value of the right list, the resulting list has every possible combination of applying a function from left list to a value in the right one.
ghci> [(*0),(+100),(^2)] <*> [1,2,3] [0,0,0,101,102,103,1,4,9]and yet another like this:
ghci> [(+),(*)] <*> [1,2] <*> [3,4] [4,5,5,6,3,4,6,8]we will compare that of a list comprehension to that of applicative ways .
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]] [16,20,22,40,50,55,80,100,110]we can do this:
ghci> (*) <$> [2,5,10] <*> [8,10,11] [16,20,22,40,50,55,80,100,110]and you can easily chain operation to the Applicative functors, such as
ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11] [55,80,100,110]
instance Applicative IO where pure = return a <*> b = do f <- a x <- b return (f x)
myAction :: IO String myAction = do a <- getLine b <- getLine return $ a ++ banother way is writing as follow.
myAction :: IO String myAction = (++) <$> getLine <*> getLineif we regress to the box analogy, we can image getLine as a box that will go out into the real world and fetch us a string. Doing (++) <$> getLine <*> getLine makes a new, bigger box that sends those two boxes out to fetch lines from the terminal and then presents the concatenation of those two lines as its result.
instance Applicative ((->) r) where pure x = (\_ -> x) f <*> g = \x -> f x (g x)let's see some exapmle of Appicate on the -> r
ghci> (pure 3) "blah" 3this makes it even more hard to understand
ghci> pure 3 "blah" 3a more real and obscure example is like this
ghci> :t (+) <$> (+3) <*> (*100) (+) <$> (+3) <*> (*100) :: (Num a) => a -> a ghci> (+) <$> (+3) <*> (*100) $ 5 508
instance Applicative ZipList where pure x = ZipList (repeat x) ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100] [101,102,103] ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..] [101,102,103] ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2] [5,3,3,4] ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat" [('d','c','r'),('o','a','a'),('g','t','t')]there is also an liftA2 function defined in the Control.Applicative module. the type of the liftA2 is liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
ghci> liftA2 (:) (Just 3) (Just [4]) Just [3,4] ghci> (:) <$> Just 3 <*> Just [4] Just [3,4]
-- file -- applicative_sequence.hs -- description: -- implement a sequence applicative import Control.Applicative sequenceA :: (Applicative f) => [f a] -> f [a] sequenceA [] = pure [] sequenceA (x:xs) = (:) <$> x <*> sequenceA xs -- no surprise, this is a recursive implementation of sequenceA -- another way to implement this to use the foldr mehod sequenceA' :: (Applicative f) => [f a] -> f[a] sequenceA' [] = pure [] sequenceA' = foldr (liftA2 (:)) (pure [])
ghci> map (\f -> f 7) [(>4),(<10),odd]
[True,True,True]
ghci> and $ map (\f -> f 7) [(>4),(<10),odd]
True
-- replaced with the sequenceA methods
ghci> sequenceA [(>4),(<10),odd] 7
[True,True,True]
ghci> and $ sequenceA [(>4),(<10),odd] 7
True
with sequenceA to simulate the method call of the list comprehension .ghci> sequenceA [[1,2,3],[4,5,6]] [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] ghci> [[x,y] | x <- [1,2,3], y <- [4,5,6]] [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]and sequenceA on the IO actions
ghci> sequenceA [getLine, getLine, getLine] heyh ho woo ["heyh","ho","woo"]