Playlists
> [1,2] + [3,4]
[4,5]
> (Just 3) + (Just 2)
Just 5
fmap0 :: a -> f a
fmap1 :: (a -> b) -> f a -> f b
fmap2 :: (a -> b -> c) -> f a -> f b -> f c
fmap3 :: (a -> b -> c -> d) -> f a -> f b -> f c -> f d
> fmap2 (+) [1,2] [3,4]
[4,5]
> fmap2 (+) (Just 3) (Just 2)
Just 5
pure :: a -> f a
aplica :: f (a -> b) -> f a -> f b
fmap0 :: a -> fa
fmap0 = pure
fmap1 :: (a -> b) -> (f a -> f b)
fmap1 g x = aplica (pure g) x
fmap2 :: (a -> (b -> c)) -> (f a -> (f b -> f c))
fmap2 g x y = aplica (aplica (pure g) x) y
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
> pure (+) <*> [1,2] <*> [3,4] -- não dá esse resultado
[4,6]
> pure (+) <*> (Just 3) <*> (Just 2)
Just 5
pure
nesse contexto é a de que estamos
transformando uma função pura em um determinado contexto
computacional (de computação não determinística, de computação
que pode falhar, etc.)instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just g) <*> mx = fmap g mx
r1 = maybeDiv x y
r2 = maybeDiv y x
-- Se alguma divisão falhar, retorna Nothing
-- Não precisamos criar um maybeAdd!
somaResultados = pure (+) <*> r1 <*> r2
pure (+) <*> maybeDiv 1 0 <*> maybeDiv 0 1
Saída:
instance Applicative [] where
pure x = [x]
gs <*> xs = [g x | g <- gs, x <- xs]
pure (+1) <*> [1,2,3]
Saída:
pure (+) <*> [1] <*> [2]
Saída:
pure (*) <*> [1,2] <*> [3,4]
Saída:
pure (++) <*> ["ha","heh","hmm"] <*> ["?","!","."]
Saída:
x * y
, mas tanto x
quanto y
são não-determinísticos, ou seja, podem assumir uma
lista de possíveis valores.pure (*) <*> [1,2,3] <*> [2,3]
Saída:
pure (*) <*> [1,2,3] <*> []
Saída:
-- Não dá esse resultado. Veja ZipList.
> pure (+) <*> [1,2,3] <*> [4,5]
[5,7]
import Control.Applicative
pure (+) <*> ZipList [1,2,3] <*> ZipList [4,5]
Saída:
SafeNum
+ Applicative
A instância de Applicative
para SafeNum
é bem simples:
instance Applicative SafeNum where
pure = SafeNum
f <*> x = flatten $ fmap (`fmap` x) f
-- Versão "pura"
f0 :: Int -> Int -> SafeNum Int
f0 x y
| isSafe xy && isSafe yx = safeAdd (unbox xy) (unbox yx)
| (not.isSafe) xy = xy
| otherwise = yx
where
xy = safeDiv x y
yx = safeDiv y x
unbox (SafeNum a) = a
isSafe (SafeNum _) = True
isSafe _ = False
-- Versão com functors
f1 :: Int -> Int -> SafeNum Int
f1 x y =
let xy = safeDiv x y
yx = safeDiv y x
safeAddXY = fmap safeAdd xy
safeXYPlusYX = fmap (`fmap` yx) safeAddXY
in
(flatten.flatten) safeXYPlusYX
-- Versão com applicative
f2 :: Int -> Int -> SafeNum Int
f2 x y =
let xy = safeDiv x y -- SafeNum Int
yx = safeDiv y x -- SafeNum Int
in
flatten $ pure safeAdd <*> xy <*> yx
Será que dá pra fazer melhor?
g
a
ser aplicada na ordem:g :: a -> Maybe a
[g x1, g x2, g x3]
pure (:) <*> g x1 <*>
(pure (:) <*> g x2 <*>
(pure (:) <*> g x3 <*> pure []))
g x2
falhe, podemos retornar
Nothing
imediatamente.-- sequencia de Applicatives
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = pure (:) <*> x <*> sequenceA xs
sequenceA [Just 3, Just 2, Just 1]
Saída:
sequenceA [Just 3, Nothing, Just 1]
Saída:
sequenceA [[1,2,3],[4,5,6]]
Saída:
sequenceA [[1,2,3],[4,5,6],[3,4,4],[]]
Saída:
Uma última classe que veremos no curso é a Traversable
ou seja, tipos
que podem ser mapeados:
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f =>
(a -> f b) -> t a -> f (t b)
a
para Maybe b
e temos uma lista de a
.Maybe [b]
ao invés de [Maybe b]
. Isso dá para ser feito utilizando o Applicative
para
listas:class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f =>
(a -> f b) -> t a -> f (t b)
instance Traversable [] where
traverse g [] = pure []
traverse g (x:xs) = pure (:) <*> g x <*> traverse g xs
Supondo a função:
dec :: Int -> Maybe Int
dec x | x <= 0 = Nothing
| otherwise = Just (x - 1)
traverse dec [1,2,3]
Saída:
traverse dec [2,1,0]
Saída:
Escreva a instância de Traversable
para Tree
.
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving Show
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f =>
(a -> f b) -> t a -> f (t b)
Escreva a instância de Traversable
para Tree
:
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving Show
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f =>
(a -> f b) -> t a -> f (t b)
instance Traversable Tree where
traverse g (Leaf x) = pure Leaf <*> g x
traverse g (Node l r) =
pure Node <*> traverse g l <*> traverse g r
Foldable
criamos um tipo Fold
:{-# LANGUAGE ExistentialQuantification #-}
data Fold i o = forall m . Monoid m => Fold (i -> m) (m -> o)
Functor
:instance Functor (Fold i) where
fmap k (Fold toMonoid summarize) = Fold toMonoid (k . summarize)
Applicative
:instance Applicative (Fold i) where
pure o = Fold (\_ -> ()) (\_ -> o)
Fold toMonoidF summarizeF <*> Fold toMonoidX summarizeX = Fold toMonoid summarize
where
toMonoid i = (toMonoidF i, toMonoidX i)
summarize (mF, mX) = summarizeF mF (summarizeX mX)
sum :: Num n => Fold n n
sum = Fold Sum getSum
length :: Num n => Fold i n
length = Fold (\_ -> Sum 1) getSum
average :: Fractional n => Fold n n
average = (/) <$> sum <*> length
Estes slides foram preparados para os cursos de Paradigmas de Programação e Desenvolvimento Orientado a Tipos na UFABC.
Este material pode ser usado livremente desde que sejam mantidos, além deste aviso, os créditos aos autores e instituições.