applicative-functor
slightly stronger and more useful versions of functors called applicative functors.
mapping functions over functors, we usually mapped functions that take only one parameter.
(a->b) -> f a -> f b
But what happens when we map a function like *, which takes two parameters or more parameter?
(a->b->c) -> f a -> f (b->c)
> :t map (*) (Just 5)
> Maybe (Int -> Int)
what if
f () <$> f a
map function over functor map (a->b) f a, get f b
map (a->b->c) f a , get f (b->c)
map function in functor over functor
map f (a->b) (f a) ???
class Functor f <= Apply f where
apply :: forall a b. f (a -> b) -> f a -> f b
class Apply f <= Applicative f where
pure :: forall a. a -> f a
class (Functor f) = > Applicative f where
pure :: a -> f a
(<* >) :: f (a -> b) -> f a -> f b
pure
A better way of thinking about pure would be to say that it takes a value and puts it in some sort of default (or pure) context— a minimal context that still yields that value.
apply
beefed up map
map:: (a->b) -> f a -> f b apply:: f (a->b) -> fa -> f b f that plays the role of the applicative functor should take one concrete type as a parameter,
> Just (3 * _) <*> Just 8
(Just 24)
> Just ((*) 3) <*> Just 8
(Just 24)
> pure (3 * _) <*> Just 8
(Just 24)
> pure (*) <*> Just 3 <*> Just 8
> (Just 24)
> pure (*) <*> Just 3 <*> Nothing
Nothing
pure (_) <_> Just 3 , will get Just (3 \* \_), and examine its type as below:
> :t pure (*) <*> Just 3
Maybe (Int -> Int)
This is because of 'partial application'
The function can take as many parameters as we want, because it's always partially applied step by step between occurences of <*>.
pure f <*> x = f <$> x
f: normal function x: Functor -> (partial) applied function inside Functor
one param
> pure (3 * _) <*> Just 8
> (Just 24)
> (3 * _) <$> Just 8
(Just 24)
two params
> :t pure (*) <*> Just 3
> Maybe (Int -> Int)
> :t (*) <$> Just 3
> Maybe (Int -> Int)
(< $ >) :: (Functor f) = > (a -> b) -> f a -> f b
f < $ > x = fmap f x
type variables are independent of parameter names or other value names.
- The f in the function declaration here is a type variable with a class constraint saying that any type constructor that replaces f should be in the Functor typeclass. The f in the function body denotes a function that we map over x. The fact that we used f to represent both of those doesn't mean that they somehow represent the same thing.
applicative style shines!
if we want to apply a function f between three applicative functors, we can write f < $ > x <_> y <_> z.
If the parameters weren't applicative functors but normal values, we'd write f x y z.
> (<>) <$> Just "Hello" <*> Just " World"
(Just "Hello World")
> (<>) "Hello" " World"
"Hello World"
use applicative style on Array to replace Array Comprehension
> :paste
… result1 = do
… x <- [2, 5, 10]
… y <- [8, 10, 11]
… pure (x * y)
…
> result1
[16,20,22,40,50,55,80,100,110]
> result2 = (*) <$> [2, 5, 10] <*> [8, 10, 11]
> result2
[16,20,22,40,50,55,80,100,110]
> filter (_ > 50) $ (*) <$> [2, 5, 10] <*> [ 8, 10, 11]
[55,80,100,110]