OU COMO
PROGRAMAÇÃO FUNCIONAL É UMA ÓTIMA FERRAMENTA PARA CONTROLE
DE COMPLEXIDADE.
PROGRAMAS E FUNÇÕES
SOBRE
PENSANDO EM CORRETUDE,
Pessoas cometem erros
Programadores são pessoas
∴ Programadores cometem erros
Ferramentas e linguagens importam;
Ajudam a expressar, abstrair e construir.
Arthur Xavier
Estudante de Ciência da Computação;
Apaixonado por programação funcional;
Membro ativo da comunidade FP;
Desenvolvendo com Phil Freeman uma formalização
de interfaces de usuário declarativas com Comonads;
OU COMO
PROGRAMAÇÃO FUNCIONAL É UMA ÓTIMA FERRAMENTA PARA CONTROLE
DE COMPLEXIDADE.
PROGRAMAS E FUNÇÕES
SOBRE
PENSANDO EM ABSTRAÇÃO,
Outline
Por que programação funcional;
Abstrações funcionais;
Complexidade sem dificuldade:
1. Monóides;
2. Mônadas.
Corretude & Abstração
Corretude?
comando1();
comando2(); // mudança de estado
variável = comando3(); // estado privado
if (variável.atributo)
exceção!
envia_email(); // mudança de estado
lança_míssil();
// return variável;
Como garantir corretude dessa forma?
Linguagens convencionais
Semântica sequencial de transições de estado;
Divisão entre mundo das expressões vs. comandos;
Dificuldade de combinar programas;
Ausência de propriedades matemáticas.
Abstração?
Valores; (imutáveis)
Funções; (puras)
Funções são valores!
Funções de ordem superior!
// users = usernames.map(getAccount)
users.map(numberOfPosts).reduce(maximum, 0)
=
users.reduce(maximum ∘ numberOfPosts, 0)
// users = map(usernames, getAccount)
reduce(maximum, 0, map(numberOfPosts, users))
=
reduce(maximum ∘ numberOfPosts, 0, users)
HASKELL
-- users = map usernames getAccount
reduce maximum 0 (map numberOfPosts users)
=
reduce (maximum . numberOfPosts) 0 users
Composição.
Composição de funções ⇒ composição de programas.
Corretude ⟺ Abstração
Corretude por construção.
~ Abstração ~
O que faz uma boa abstração?
Intuitividade & Universalidade
Matemática!
Álgebra abstrata & Teoria de categorias
MONÓIDES
Conjunto S com uma operação binária •, tal que
1. ∀a,b ∈ S : a•b ∈ S. (fechamento)
2. ∀a,b,c ∈ S : (a•b)•c = a•(b•c). (associatividade)
3. ∃e ∈ S : ∀a ∈ S : e•a = a•e = a. (identidade)
Acumulador com “elemento neutro”;
(a + b) + c = a + (b + c).
a + 0 = 0 + a = a.
(a × b) × c = a × (b × c).
a × 1 = 1 × a = a.
max(max(a, b), c) = max(a, max(b, c)).
max(a, 0) = max(0, a) = a. (a > 0)
Monóides são ubíquos.
Yorgey, B. A. (2012). Monoids: theme and variations (functional pearl).
connectionBuilder
.withHost("myhost.com")
.withRemote("remote.com")
.withAuth(PASSWORD)
.build()
recipe =
withHost("myhost.com")
+ withRemote("remote.com")
+ withAuth(PASSWORD)
build(recipe)
Mas e daí?
O que isso traz? O que isso abstrai?
Dividir para conquistar.
(a•b)•c
=
((a1
•a2
•a3
)•(b1
•b2
))•(c1
•c2
)
=
(((a11
•a12
)•a2
•(a31
•a32
• …))•(b1
•b2
))•(c1
•(c11
•c12
• …))
Paralelismo & Incrementalidade
(a•b)•c
=
((a1
•a2
•a3
)•(b1
•b2
))•(c1
•c2
)
=
(((a11
•a12
)•a2
•(a31
•a32
• …))•(b1
•b2
))•(c1
•(c11
•c12
• …))
HASKELL
class Monoid s where
(<>) :: s -> s -> s
mempty :: s
Mas e as leis?
HASKELL
(a <> b) <> c ≡ a <> (b <> c)
mempty <> a ≡ a
a <> mempty ≡ a
Não tem como garantir.
¯_(ツ)_/¯
HASKELL
data Add = Add Int
instance Monoid Add where
Add a <> Add b = Add (a + b)
mempty = Add 0
…
data Mult = Mult Int
instance Monoid Mult where
Mult a <> Mult b = Mult (a * b)
mempty = Mult 1
HASKELL
foldMap :: Monoid m => (a -> m) -> [a] -> m
foldMap f [] = mempty
foldMap f (a:as) = f a <> foldMap f as
HASKELL
reduce maximum 0 (map numberOfPosts users)
=
reduce (maximum . numberOfPosts) 0 users
≅
foldMap (Max . numberOfPosts) users
HASKELL
> foldMap (Add . numberOfPosts) [user1, user2, …]
Add 98
> foldMap (Max . numberOfPosts) [user1, user2, …]
Max 10
> foldMap (Min . numberOfPosts) [user1, user2, …]
Min 1
> foldMap (Max . numberOfPosts) []
Max (-9223372036854775808)
HASKELL
data Maybe a = Nothing | Just a
instance Monoid a => Monoid (Maybe a) where …
…
> foldMap (Just . Max . numberOfPosts) []
Nothing
Exemplo: builder
HASKELL
data Builder a = Builder (a -> a)
instance Monoid (Builder a) where
Builder f <> Builder g = Builder (f . g)
mempty = Builder id
build :: a -> Builder a -> a
build def (Builder builder) = builder def
HASKELL
conn :: Connection
conn = build defaultConnection recipe
where
recipe =
withHost "myhost.com"
<> withRemote "remote.com"
<> withAuth Password
Disjunção de predicados;
Conjunção de predicados;
Conjuntos com união;
Composição de opções;
Combinação de regras;
Interseção de formas bidimensionais;
Funções que retornam monóides;
Combinação de máquinas de estado;
MÔNADAS
Pra quê?
Exemplo: logging
HASKELL
add1 :: Int -> (Int, String)
add1 x = (x + 1, "+1")
mul3 :: Int -> (Int, String)
mul3 x = (x * 3, "*3")
-- deseja-se
add1 30 ?? mul3 ?? add1 = (94, "+1*3+1")
-- mas o que é (??)
HASKELL
add1 :: Int -> (Int, String)
add1 x = (x + 1, "+1")
mul3 :: Int -> (Int, String)
mul3 x = (x * 3, "*3")
-- ou ainda
do
x <- add1 30
y <- mul3 x
add1 y
HASKELL
add1 :: Int -> (Int, String)
add1 x = (x + 1, "+1")
mul3 :: Int -> (Int, String)
mul3 x = (x * 3, "*3")
-- deseja-se
add1 30 ?? mul3 ?? add1 = (94, "+1*3+1")
-- mas o que é (??)
HASKELL
(??) :: (Int, String) -> (Int -> (Int, String)) -> (Int, String)
Produção de contexto na saída;
Impureza na saída;
Sequencialidade.
Tipo m com duas operações pure e bind
pure :: a -> m a
bind :: m a -> (a -> m b) -> m b
(>>=) = bind
HASKELL
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
pure :: a -> m a
HASKELL
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
f >=> g = (f x) >>= g
HASKELL
(f >=> g) >=> h ≡ f >=> (g >=> h)
pure >=> f ≡ f
f >=> pure ≡ f
HASKELL
(a <> b) <> c ≡ a <> (b <> c)
mempty <> a ≡ a
a <> mempty ≡ a
“A monad is just a monoid in the category of endofunctors,
what’s the problem?”
Writer: logging
a -> (b, String)
HASKELL
data Writer a = Writer (a, String)
instance Monad Writer where
pure a = Writer (a, "")
Writer (a, s) >>= f = Writer (b, s ++ s2)
where
Writer (b, s2) = f a
Writer: logging
Monoid t => a -> (b, t)
HASKELL
data Writer t a = Writer (a, t)
instance Monoid t => Monad (Writer t) where
pure a = Writer (a, mempty)
Writer (a, t) >>= f = Writer (b, t <> t2)
where
Writer (b, t2) = f a
HASKELL
add1 :: Int -> Writer String Int
add1 x = Writer (x + 1, "+1")
mul3 :: Int -> Writer String Int
mul3 x = Writer (x * 3, "*3")
add1 30 >>= mul3 >>= add1 = (94, "+1*3+1")
HASKELL
add1 :: Int -> Writer String Int
add1 x = Writer (x + 1, "+1")
mul3 :: Int -> Writer String Int
mul3 x = Writer (x * 3, "*3")
do
x <- add1 30
y <- mul3 x
add1 y
HASKELL
tell :: Monoid t => t -> Writer t ()
tell t = Writer ((), t)
add1 x = do { tell "+1"; pure (x + 1) }
mul3 x = do { tell "*3"; pure (x * 3) }
do
x <- add1 30
y <- mul3 x
add1 y
State: estado
a -> (s -> (b, s))
≅
(a, s) -> (b, s)
HASKELL
data State s a = State (s -> (a, s))
instance Monad (State s) where …
put :: s -> State s ()
put s = _ -> ((), s)
get :: State s s
get = s -> (s, s)
modify :: (s -> s) -> State s ()
modify f = s -> ((), f s)
HASKELL
put :: s -> State s ()
get :: State s s
modify :: (s -> s) -> State s ()
…
add1 x = do
modify (log -> log <> "+1")
pure (x + 1)
mul3 x = do
modify (log -> log <> "*3")
pure (x * 3)
Maybe: falha
a -> Maybe b
HASKELL
data Maybe a = Nothing | Just a
instance Monad Maybe where …
head :: [a] -> Maybe a
…
tryGetLastPhoto username = do
user <- tryGetUser username
photos <- tryGetPhotos user
head photos
Padrão universal quando se trata de computações.
Reader: ambiente global
a -> (r -> b)
≅
(a, r) -> b
List: não-determinismo
a -> [b]
Dist: não-determinismo com probabilidades
a -> [(b, Float)]
Parser: parsing…
a -> (String -> [(b, String)])
≅
(a, String) -> [(b, String)]
Cont: continuações
a -> ((b -> r) -> r)
≅
(a -> r) -> (b -> r)
IO: efeitos colaterais
a -> (RealWorld -> (b, RealWorld))
≅
(a, RealWorld) -> (b, RealWorld)
Computações & Algoritmos
Sequencialidade;
Do ponto de vista interno,
permitem efeitos colaterais;
Sequência de computações são executadas
e têm seus resultados e efeitos combinados no final;
Mônadas abstraem programas!
Muitas abstrações funcionais são sobre composição.
Leis + composição ⇒ corretude por construção.
Mais?
Funtores aplicativos;
Free monad;
Monad transformers;
Comônadas;
Lentes;
Categorias;
Contêineres indexados;
Esquemas de recursão;
λ
@arthurxavierx @arthur-xavier

Sobre programas e funções: pensando em abstração