Desvendando os mistérios
das Mônades
Aprenda a domar os efeitos colaterais com Mônades em Haskell
Sobre o palestrante
Eduardo Sato
Engenheiro de Computação (Poli USP 2004)
Consultoria, desenvolvimento Java, C++, python, perl, Haskell, C etc
Músico amador: violão
Linguagens de programação, programar emuladores, compiladores
Pesquisa
- Quem aqui programa em Haskell?
- Quem aqui usa/conhece Mônades em Haskell ou outra linguagem?
- Palestra do Roberto "Haskell 101"
Quantidade de tutorias de Mônades
https://wiki.haskell.org/Monad_tutorials_timeline
A falácia do Tutorial de Mônades
Mônades?
Mônade: definição
Uma mônade nada mais é que: “um monóide na categoria dos endofunctores”
Problem?
A falácia do Tutorial de Mônades
- Aprendizado: começar com o concreto e ir ao abstrato
- Seres humanos: bons em reconhecimento de padrões
- Mônades, OO etc: aprendizado não linear e dependências circulares
- Momento “a-ha!”
- No caso das mônades: vários momentos “a-ha!”
Pureza
Linguagens funcionais
- Puras: Miranda, Haskell
- Impuras: Scheme, F#, Standard ML etc
Pureza:
- Trânsparência referencial
- Fácil de analisar
- Preguiça (laziness) manteve a Haskell pura
Entrada / Saída / Efeitos
Programas compostos por funções matemáticas:
Como fazer entrada/saída/efeitos colaterais?
Qual é a utilidade de um programa totalmente puro?
Mônades em programação funcional
Aplicações:
- Descrever computações componíveis (?) (1)
- Sequência: Separação da linha do tempo da computação composta da
linha do tempo de excecução (2)
- Contexto: Implicitamente carregar dados extras (contexto), pertinentes à
computação em si, além do seu resultado (3)
- Suplementar cálculos puros com E/S, ambiente, estado etc (4)
- E muito mais!
Expressividade: SICP - Abel & Sussman
Linguagens servem como framework pelo qual organizamos nossas ideias
sobre processos
Uma linguagem de programação poderosa possui:
- Expressões primitivas
- Meios de combinação: pelos quais elementos compostos são construídos
a partir de elementos mais simples
- Meios de abstração: pelos quais elementos compostos podem ser
nomeados e manipulados de maneira unitária
Haskell: curso relâmpago
Assinatura
Definição
Chamada de função
Definição de Tipo
Polimorfismo paramétrico
f 1 g "foo" "bar"
f :: Int -> Int g :: String -> String -> String
f n = n + 1 g a b = a ++ b
data Naipe = Paus | Copas | Espada | Ouro deriving Show
length :: [a] -> Int
Haskell: curso relâmpago
Descontstrução
data Maybe a = Just a | Nothing
foo = Just "Foo"
nada = Nothing
segredo = Just 42
showMaybe :: (Show a) => Maybe a -> String
showMaybe (Just x) = "Just " ++ show x
showMaybe Nothing = "Nothing"
ghci> showMaybe foo
"Just Foo"
ghci> showMaybe nada
"Nothing"
ghci> showMaybe segredo
"Just 42"
Motivação: um simples avaliador:
Tema e variações
Tema: o avaliador básico
data Termo = Const Int | Div Termo Termo deriving (Show)
avaliar :: Termo -> Int
avaliar (Const n) = n
avaliar (Div t n) = avaliar t `div` avaliar n
expr = Div (Div (Const 1932) (Const 2))
(Const 23)
divisaoPorZero = Div (Const 1) (Const 0)
resposta = avaliar expr
erro = avaliar divisaoPorZero
Div
Div Const
23Const
1932
Const
2
Div
Const
1
Const
0
Tema: resultado
ghci> resposta
42
ghci> erro
*** Exception: divide by zero Ops!
Variação 1
Tratamento de Erros
data Termo = Const Int | Div Termo Termo deriving (Show)
data M a = Valor a | Exc String deriving (Show)
avaliar :: Termo -> M Int
avaliar (Const n) = Valor n
avaliar (Div t u) = case avaliar t of
Exc e -> Exc e
Valor a -> case avaliar u of
Exc e -> Exc e
Valor b -> if b == 0
then Exc "divisao por zero"
else Valor (a `div` b)
Resultado
ghci> resposta
Valor 42
ghci> erro
Exc "divisao por zero"
Variação 2: prelúdio
Composição de lambdas para carregar estado:
:: s -> (Int, s)
Podemos agregar computações que “alteram” o estado e produzem
resultados parciais em uma única computação que produz um resultado
final e um estado
s: estado Int: resultado
Lambda: uma computação que recebe um estado
produzindo um resultado e um novo estado
O resultado (Int) pode ser calculado através
de um valor do fechamento (closure)
Variação 2
Estado: contar o número de divisões
data Termo = Const Int | Div Termo Termo deriving (Show)
data M a = Estado { obterEstado :: (Int -> (a, Int)) }
avaliar :: Termo -> M Int
avaliar (Const a) = Estado (x -> (a, x))
avaliar (Div t u) = Estado $ x -> let (a, y) = obterEstado (avaliar t) x
(b, z) = obterEstado (avaliar u) y
in (a `div` b, z + 1)
expr = Div (Div (Const 1932) (Const 2)) (Const 23)
resposta = obterEstado (avaliar expr) 0
obterEstado x: descasca o
valor de M obtendo a função
encapsulada
Resultado
ghci> resposta
(42,2)
Variação 3
Saída: imprimir log
data Termo = Const Int | Div Termo Termo deriving (Show)
data M a = Saida (String, a) deriving (Show)
avaliar :: Termo -> M Int
avaliar (Const a) = Saida (imprimirLinha (Const a) a, a)
avaliar (Div t u) = let Saida (x, a) = avaliar t
Saida (y, b) = avaliar u
in Saida (x ++ y ++ imprimirLinha (Div t u) (a `div` b), a `div` b)
imprimirLinha :: Termo -> Int -> String
imprimirLinha t a = show t ++ " = " ++ show a ++ "n"
Variação 3 - Cont.
expr = Div (Div (Const 1932) (Const 2))
(Const 23)
Saida (saida, resposta) = avaliar expr
Resultado
ghci> putStr saida
Const 1932 = 1932
Const 2 = 2
Div (Const 1932) (Const 2) = 966
Const 23 = 23
Div (Div (Const 1932) (Const 2)) (Const 23) = 42
ghci> print resposta
42
Mônades: o que são
Na prática (em Haskell):
- Uma declaração de tipo parametrizado em a para representar
computações:
- data M a = ...
- Função return: transformar valores a em computações:
- return :: a -> M a (sem efeitos, exceções, log, mudar estado)
- Uma maneira de combinar computações
- Operador bind: (>>=) :: M a -> (a -> M b) -> M b
m k
Leis
- Identidade:
- return a >>= f = f a
- m >>= return = m
- Associatividade:
- (m >>= f) >>= g = m >>= (x -> f x >>= g)
Tema revisitado
Mônade Identidade
data Termo = Const Int | Div Termo Termo deriving (Show)
data Identity a = Identity a deriving (Show)
instance Monad Identity where
return x = Identity x
(Identity a) >>= k = k a
avaliar :: Termo -> Identity Int
avaliar (Const a) = return a
avaliar (Div t u) = avaliar t >>= a ->
avaliar u >>= b ->
return (a `div` b)
avaliar t >>= (a -> avaliar u >>= (b -> return (a `div` b)))
Tema revisitado
expr = Div (Div (Const 1932) (Const 2))
(Const 23)
resposta = avaliar expr
ghci> resposta
Identity 42
Variação 1 revistada
Exceções
data Termo = Const Int | Div Termo Termo deriving (Show)
data Exception a = Raise String | Return a deriving (Show)
instance Monad Exception where
return x = Return x
(Raise e) >>= f = Raise e
(Return a) >>= f = f a
raise :: String -> Exception a
raise e = Raise e
Variação 1 revistada
avaliar :: Termo -> Exception Int
avaliar (Const a) = return a
avaliar (Div t u) = avaliar t >>= a ->
avaliar u >>= b ->
if b == 0
then raise "divisao por zero"
else return (a `div` b)
expr = Div (Div (Const 1932) (Const 2)) (Const 23)
divisaoPorZero = Div (Const 1) (Const 0)
resposta = avaliar expr
erro = avaliar divisaoPorZero
Resultado
ghci> resposta
Return 42
ghci> erro
Raise "divisao por zero"
Variação 2 revistada
data Termo = Const Int | Div Termo Termo deriving (Show)
data State a = State { runState :: (Int -> (a, Int)) }
instance Monad State where
return x = State (s -> (x, s))
m >>= k = State (x -> let (a, y) = runState m x
(b, z) = runState (k a) y
in (b, z))
tick :: State ()
tick = State (s -> ((), s + 1))
Variação 2 revistada
avaliar :: Termo -> State Int
avaliar (Const a) = return a
avaliar (Div t u) = avaliar t >>= a ->
avaliar u >>= b ->
tick >>= _ ->
return (a `div` b)
expr = Div (Div (Const 1932) (Const 2)) (Const 23)
resposta = runState (avaliar expr) 0
Resultado
ghci> resposta
(42,2)
Variação 3 revistada
data Termo = Const Int | Div Termo Termo deriving (Show)
data Writer a = Writer { runWriter :: (String, a) } deriving (Show)
instance Monad Writer where
return x = Writer ("", x)
Writer (x, a) >>= k = let Writer (y, b) = k a
in Writer (x ++ y, b)
output :: String -> Writer ()
output s = Writer (s, ())
imprimirLinha :: Termo -> Int -> String
imprimirLinha t a = show t ++ " = " ++ show a ++ "n"
Variação 3 revistada
avaliar :: Termo -> Writer Int
avaliar (Const a) = output (imprimirLinha (Const a) a) >>= _ ->
return a
avaliar (Div t u) = avaliar t >>= a ->
avaliar u >>= b ->
output (imprimirLinha (Div t u) (a `div` b)) >>= _ ->
return (a `div` b)
expr = Div (Div (Const 1932) (Const 2)) (Const 23)
Writer (saida, resposta) = avaliar expr
Resultado
ghci> putStr saida
Const 1932 = 1932
Const 2 = 2
Div (Const 1932) (Const 2) = 966
Const 23 = 23
Div (Div (Const 1932) (Const 2)) (Const 23) = 42
ghci> resposta
42
Notação do
ma >>= a ->
mb >>= b ->
mc >>= c ->
return x
do a <- ma
b <- mb
return x
ma >>= (a -> mb >>= (b -> mc >>= (c -> return x)))
Açúcar sintático - Exception
avaliar :: Termo -> Exception Int
avaliar (Const a) = return a
avaliar (Div t u) = do a <- avaliar t
b <- avaliar u
if b == 0
then raise "divisao por zero"
else return (a `div` b)
avaliar :: Termo -> Identity Int
avaliar (Const a) = return a
avaliar (Div t u) = do a <- avaliar t
b <- avaliar u
return (a `div` b)
Açúcar sintático
avaliar :: Termo -> State Int
avaliar (Const a) = return a
avaliar (Div t u) = do a <- avaliar t
b <- avaliar u
tick
return (a `div` b)
avaliar :: Termo -> Identity Int
avaliar (Const a) = return a
avaliar (Div t u) = do a <- avaliar t
b <- avaliar u
return (a `div` b)
Açúcar sintático - Writer
avaliar :: Termo -> Writer Int
avaliar (Const a) = do output (imprimirLinha (Const a) a)
return a
avaliar (Div t u) =
do a <- avaliar t
b <- avaliar u
output (imprimirLinha (Div t u) (a `div` b))
return (a `div` b)
avaliar :: Termo -> Identity Int
avaliar (Const a) = return a
avaliar (Div t u) = do a <- avaliar t
b <- avaliar u
return (a `div` b)
Outras Mônades
- Par: paralelismo
- Lista: não determinismo
- Cont: continuações
- Parsec: parsers
- IO
- Lógica
- Probabilidade
- Free Monads -> DSLs
- etc
IO
main = do putStrLn "Qual é o seu nome?"
nome <- getLine
putStrLn ("Olá, " ++ nome ++ "!")
putStrLn :: String -> IO ()
getLine :: IO String
Lista
do x <- [1, 2, 3]
y <- ['a', 'b']
Return (x, y)
[(1, 'a'), (2, 'b'), (3, 'c')]
[(x,y) | x <- [1,2,3], y <- ['a', 'b'])
.net LINQ
Perguntas
eduardo.sato@gmail.com

TDC2016SP - Trilha Programação Funcional

  • 1.
    Desvendando os mistérios dasMônades Aprenda a domar os efeitos colaterais com Mônades em Haskell
  • 2.
    Sobre o palestrante EduardoSato Engenheiro de Computação (Poli USP 2004) Consultoria, desenvolvimento Java, C++, python, perl, Haskell, C etc Músico amador: violão Linguagens de programação, programar emuladores, compiladores
  • 3.
    Pesquisa - Quem aquiprograma em Haskell? - Quem aqui usa/conhece Mônades em Haskell ou outra linguagem? - Palestra do Roberto "Haskell 101"
  • 4.
    Quantidade de tutoriasde Mônades https://wiki.haskell.org/Monad_tutorials_timeline
  • 5.
    A falácia doTutorial de Mônades
  • 6.
  • 7.
    Mônade: definição Uma mônadenada mais é que: “um monóide na categoria dos endofunctores”
  • 8.
  • 9.
    A falácia doTutorial de Mônades - Aprendizado: começar com o concreto e ir ao abstrato - Seres humanos: bons em reconhecimento de padrões - Mônades, OO etc: aprendizado não linear e dependências circulares - Momento “a-ha!” - No caso das mônades: vários momentos “a-ha!”
  • 10.
    Pureza Linguagens funcionais - Puras:Miranda, Haskell - Impuras: Scheme, F#, Standard ML etc Pureza: - Trânsparência referencial - Fácil de analisar - Preguiça (laziness) manteve a Haskell pura
  • 11.
    Entrada / Saída/ Efeitos Programas compostos por funções matemáticas: Como fazer entrada/saída/efeitos colaterais? Qual é a utilidade de um programa totalmente puro?
  • 12.
    Mônades em programaçãofuncional Aplicações: - Descrever computações componíveis (?) (1) - Sequência: Separação da linha do tempo da computação composta da linha do tempo de excecução (2) - Contexto: Implicitamente carregar dados extras (contexto), pertinentes à computação em si, além do seu resultado (3) - Suplementar cálculos puros com E/S, ambiente, estado etc (4) - E muito mais!
  • 13.
    Expressividade: SICP -Abel & Sussman Linguagens servem como framework pelo qual organizamos nossas ideias sobre processos Uma linguagem de programação poderosa possui: - Expressões primitivas - Meios de combinação: pelos quais elementos compostos são construídos a partir de elementos mais simples - Meios de abstração: pelos quais elementos compostos podem ser nomeados e manipulados de maneira unitária
  • 14.
    Haskell: curso relâmpago Assinatura Definição Chamadade função Definição de Tipo Polimorfismo paramétrico f 1 g "foo" "bar" f :: Int -> Int g :: String -> String -> String f n = n + 1 g a b = a ++ b data Naipe = Paus | Copas | Espada | Ouro deriving Show length :: [a] -> Int
  • 15.
    Haskell: curso relâmpago Descontstrução dataMaybe a = Just a | Nothing foo = Just "Foo" nada = Nothing segredo = Just 42 showMaybe :: (Show a) => Maybe a -> String showMaybe (Just x) = "Just " ++ show x showMaybe Nothing = "Nothing" ghci> showMaybe foo "Just Foo" ghci> showMaybe nada "Nothing" ghci> showMaybe segredo "Just 42"
  • 16.
    Motivação: um simplesavaliador: Tema e variações Tema: o avaliador básico data Termo = Const Int | Div Termo Termo deriving (Show) avaliar :: Termo -> Int avaliar (Const n) = n avaliar (Div t n) = avaliar t `div` avaliar n expr = Div (Div (Const 1932) (Const 2)) (Const 23) divisaoPorZero = Div (Const 1) (Const 0) resposta = avaliar expr erro = avaliar divisaoPorZero Div Div Const 23Const 1932 Const 2 Div Const 1 Const 0
  • 17.
    Tema: resultado ghci> resposta 42 ghci>erro *** Exception: divide by zero Ops!
  • 18.
    Variação 1 Tratamento deErros data Termo = Const Int | Div Termo Termo deriving (Show) data M a = Valor a | Exc String deriving (Show) avaliar :: Termo -> M Int avaliar (Const n) = Valor n avaliar (Div t u) = case avaliar t of Exc e -> Exc e Valor a -> case avaliar u of Exc e -> Exc e Valor b -> if b == 0 then Exc "divisao por zero" else Valor (a `div` b)
  • 19.
    Resultado ghci> resposta Valor 42 ghci>erro Exc "divisao por zero"
  • 20.
    Variação 2: prelúdio Composiçãode lambdas para carregar estado: :: s -> (Int, s) Podemos agregar computações que “alteram” o estado e produzem resultados parciais em uma única computação que produz um resultado final e um estado s: estado Int: resultado Lambda: uma computação que recebe um estado produzindo um resultado e um novo estado O resultado (Int) pode ser calculado através de um valor do fechamento (closure)
  • 21.
    Variação 2 Estado: contaro número de divisões data Termo = Const Int | Div Termo Termo deriving (Show) data M a = Estado { obterEstado :: (Int -> (a, Int)) } avaliar :: Termo -> M Int avaliar (Const a) = Estado (x -> (a, x)) avaliar (Div t u) = Estado $ x -> let (a, y) = obterEstado (avaliar t) x (b, z) = obterEstado (avaliar u) y in (a `div` b, z + 1) expr = Div (Div (Const 1932) (Const 2)) (Const 23) resposta = obterEstado (avaliar expr) 0 obterEstado x: descasca o valor de M obtendo a função encapsulada
  • 22.
  • 23.
    Variação 3 Saída: imprimirlog data Termo = Const Int | Div Termo Termo deriving (Show) data M a = Saida (String, a) deriving (Show) avaliar :: Termo -> M Int avaliar (Const a) = Saida (imprimirLinha (Const a) a, a) avaliar (Div t u) = let Saida (x, a) = avaliar t Saida (y, b) = avaliar u in Saida (x ++ y ++ imprimirLinha (Div t u) (a `div` b), a `div` b) imprimirLinha :: Termo -> Int -> String imprimirLinha t a = show t ++ " = " ++ show a ++ "n"
  • 24.
    Variação 3 -Cont. expr = Div (Div (Const 1932) (Const 2)) (Const 23) Saida (saida, resposta) = avaliar expr
  • 25.
    Resultado ghci> putStr saida Const1932 = 1932 Const 2 = 2 Div (Const 1932) (Const 2) = 966 Const 23 = 23 Div (Div (Const 1932) (Const 2)) (Const 23) = 42 ghci> print resposta 42
  • 26.
    Mônades: o quesão Na prática (em Haskell): - Uma declaração de tipo parametrizado em a para representar computações: - data M a = ... - Função return: transformar valores a em computações: - return :: a -> M a (sem efeitos, exceções, log, mudar estado) - Uma maneira de combinar computações - Operador bind: (>>=) :: M a -> (a -> M b) -> M b m k
  • 27.
    Leis - Identidade: - returna >>= f = f a - m >>= return = m - Associatividade: - (m >>= f) >>= g = m >>= (x -> f x >>= g)
  • 28.
    Tema revisitado Mônade Identidade dataTermo = Const Int | Div Termo Termo deriving (Show) data Identity a = Identity a deriving (Show) instance Monad Identity where return x = Identity x (Identity a) >>= k = k a avaliar :: Termo -> Identity Int avaliar (Const a) = return a avaliar (Div t u) = avaliar t >>= a -> avaliar u >>= b -> return (a `div` b) avaliar t >>= (a -> avaliar u >>= (b -> return (a `div` b)))
  • 29.
    Tema revisitado expr =Div (Div (Const 1932) (Const 2)) (Const 23) resposta = avaliar expr ghci> resposta Identity 42
  • 30.
    Variação 1 revistada Exceções dataTermo = Const Int | Div Termo Termo deriving (Show) data Exception a = Raise String | Return a deriving (Show) instance Monad Exception where return x = Return x (Raise e) >>= f = Raise e (Return a) >>= f = f a raise :: String -> Exception a raise e = Raise e
  • 31.
    Variação 1 revistada avaliar:: Termo -> Exception Int avaliar (Const a) = return a avaliar (Div t u) = avaliar t >>= a -> avaliar u >>= b -> if b == 0 then raise "divisao por zero" else return (a `div` b) expr = Div (Div (Const 1932) (Const 2)) (Const 23) divisaoPorZero = Div (Const 1) (Const 0) resposta = avaliar expr erro = avaliar divisaoPorZero
  • 32.
    Resultado ghci> resposta Return 42 ghci>erro Raise "divisao por zero"
  • 33.
    Variação 2 revistada dataTermo = Const Int | Div Termo Termo deriving (Show) data State a = State { runState :: (Int -> (a, Int)) } instance Monad State where return x = State (s -> (x, s)) m >>= k = State (x -> let (a, y) = runState m x (b, z) = runState (k a) y in (b, z)) tick :: State () tick = State (s -> ((), s + 1))
  • 34.
    Variação 2 revistada avaliar:: Termo -> State Int avaliar (Const a) = return a avaliar (Div t u) = avaliar t >>= a -> avaliar u >>= b -> tick >>= _ -> return (a `div` b) expr = Div (Div (Const 1932) (Const 2)) (Const 23) resposta = runState (avaliar expr) 0
  • 35.
  • 36.
    Variação 3 revistada dataTermo = Const Int | Div Termo Termo deriving (Show) data Writer a = Writer { runWriter :: (String, a) } deriving (Show) instance Monad Writer where return x = Writer ("", x) Writer (x, a) >>= k = let Writer (y, b) = k a in Writer (x ++ y, b) output :: String -> Writer () output s = Writer (s, ()) imprimirLinha :: Termo -> Int -> String imprimirLinha t a = show t ++ " = " ++ show a ++ "n"
  • 37.
    Variação 3 revistada avaliar:: Termo -> Writer Int avaliar (Const a) = output (imprimirLinha (Const a) a) >>= _ -> return a avaliar (Div t u) = avaliar t >>= a -> avaliar u >>= b -> output (imprimirLinha (Div t u) (a `div` b)) >>= _ -> return (a `div` b) expr = Div (Div (Const 1932) (Const 2)) (Const 23) Writer (saida, resposta) = avaliar expr
  • 38.
    Resultado ghci> putStr saida Const1932 = 1932 Const 2 = 2 Div (Const 1932) (Const 2) = 966 Const 23 = 23 Div (Div (Const 1932) (Const 2)) (Const 23) = 42 ghci> resposta 42
  • 39.
    Notação do ma >>=a -> mb >>= b -> mc >>= c -> return x do a <- ma b <- mb return x ma >>= (a -> mb >>= (b -> mc >>= (c -> return x)))
  • 40.
    Açúcar sintático -Exception avaliar :: Termo -> Exception Int avaliar (Const a) = return a avaliar (Div t u) = do a <- avaliar t b <- avaliar u if b == 0 then raise "divisao por zero" else return (a `div` b) avaliar :: Termo -> Identity Int avaliar (Const a) = return a avaliar (Div t u) = do a <- avaliar t b <- avaliar u return (a `div` b)
  • 41.
    Açúcar sintático avaliar ::Termo -> State Int avaliar (Const a) = return a avaliar (Div t u) = do a <- avaliar t b <- avaliar u tick return (a `div` b) avaliar :: Termo -> Identity Int avaliar (Const a) = return a avaliar (Div t u) = do a <- avaliar t b <- avaliar u return (a `div` b)
  • 42.
    Açúcar sintático -Writer avaliar :: Termo -> Writer Int avaliar (Const a) = do output (imprimirLinha (Const a) a) return a avaliar (Div t u) = do a <- avaliar t b <- avaliar u output (imprimirLinha (Div t u) (a `div` b)) return (a `div` b) avaliar :: Termo -> Identity Int avaliar (Const a) = return a avaliar (Div t u) = do a <- avaliar t b <- avaliar u return (a `div` b)
  • 43.
    Outras Mônades - Par:paralelismo - Lista: não determinismo - Cont: continuações - Parsec: parsers - IO - Lógica - Probabilidade - Free Monads -> DSLs - etc
  • 44.
    IO main = doputStrLn "Qual é o seu nome?" nome <- getLine putStrLn ("Olá, " ++ nome ++ "!") putStrLn :: String -> IO () getLine :: IO String
  • 45.
    Lista do x <-[1, 2, 3] y <- ['a', 'b'] Return (x, y) [(1, 'a'), (2, 'b'), (3, 'c')] [(x,y) | x <- [1,2,3], y <- ['a', 'b']) .net LINQ
  • 46.