Skip to main content

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]