Programación Funcional Alberto Pardo Marcos Viera Instituto de Computación, Facultad de Ingenierı́a Universidad de la República, Uruguay Alberto Pardo, Marcos Viera Programación Funcional Mónadas Modelado de efectos computacionales a través de mónadas. Ejemplos de efectos: Entrada-salida Estado No determinismo Fallas Excepciones Alberto Pardo, Marcos Viera Programación Funcional Evaluador de Expresiones data Exp = Num Int | Add Exp Exp eval :: Exp → Int eval (Num n) = n eval (Add x y ) = eval x + eval y Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Fallas Supongamos que deseamos agregar un operador de división a nuestras expresiones: data Exp = Num Int | Add Exp Exp | Div Exp Exp La operación de división debe controlar ahora el caso excepcional de división por cero. data Maybe a = Just a | Nothing divM :: Int → Int → Maybe Int a ‘divM‘ b = if b ≡ 0 then Nothing else Just (a ‘div ‘ b) Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Fallas (2) eval :: Exp → Maybe Int eval (Num n) = Just n eval (Add x y ) = case eval x of Nothing → Nothing Just a → case eval y of Nothing → Nothing Just b → Just (a + b) eval (Div x y ) = case eval x of Nothing → Nothing Just a → case eval y of Nothing → Nothing Just b → a ‘divM‘ b Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Fallas (3) Definamos: return :: a → Maybe a return a = Just a (>>=) :: Maybe a → (a → Maybe b) → Maybe b m >>= f = case m of Nothing → Nothing Just a → f a Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Fallas (4) Entonces, eval :: Exp → Maybe Int eval (Num n) = return n eval (Add x y ) = eval x > >= λa → eval y > >= λb → return (a + b) eval (Div x y ) = eval x > >= λa → eval y > >= λb → a ‘divM‘ b Alberto Pardo, Marcos Viera Programación Funcional La clase Monad class Monad m where (>>=) :: m a → (a → m b) → m b (>>) :: m a → m b → m b return :: a → m a fail :: String → m a m >> k = m >>= \ → k fail s = error s Alberto Pardo, Marcos Viera Programación Funcional Mónada Maybe data Maybe a = Just a | Nothing instance Monad Maybe where return = Just m >>= k = case m of Just x → k x Nothing → Nothing fail = Nothing Alberto Pardo, Marcos Viera Programación Funcional Leyes de mónadas return x >>= f = f x m >>= return = m (m >>= f ) >>= g = m> >= λx → (f x >>= g ) Alberto Pardo, Marcos Viera Programación Funcional Composición de funciones monádicas (•) :: Monad m ⇒ (b → m c) → (a → m b) → a → m c (f • g ) a = g a > >= f Propiedades: f • return = f return • f = f (f • g ) • h = f • (g • h) Se prueban facilmente usando las leyes de mónadas. Alberto Pardo, Marcos Viera Programación Funcional Notación do do m =m do {x ← m; m0 } = m > >= λx → do m0 do {m; m0 } =m> > do m0 Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Fallas (notación do) eval :: Exp → Maybe Int eval (Num n) = return n eval (Add x y ) = do a ← eval x b ← eval y return (a + b) eval (Div x y ) = do a ← eval x b ← eval y a ‘divM‘ b Alberto Pardo, Marcos Viera Programación Funcional Mónada Exception data Exceptional e a = Success a | Exception e instance Monad (Exceptional e) where return = Success Exception e >>= = Exception e Success a >>= f = f a throw :: e → Exceptional e a throw = Exception catch :: Exceptional e a → (e → Exceptional e a) → Exceptional e a catch (Exception e) h = h e catch (Success a) = Success a Alberto Pardo, Marcos Viera Programación Funcional Funciones sobre Mónadas (ver Control.Monad) sequence :: Monad m ⇒ [m a] → m [a] sequence [ ] = return [ ] sequence (m : ms) = do x ← m xs ← sequence ms return (x : xs) filterM :: (Monad m) ⇒ (a → m Bool) → [a] → m [a] = return [ ] filterM [ ] filterM p (x : xs) = do b ← p x ys ← filterM p xs return (if b then x : ys else ys) Alberto Pardo, Marcos Viera Programación Funcional Funciones sobre Mónadas (ver Control.Monad) addM :: (Num a, Monad m) ⇒ m a → m a → m a addM m m0 = do x ← m y ← m0 return (x + y ) liftM2 :: (a → b → c) → m a → m b → m c liftM2 f m m0 = do x ← m y ← m0 return (f x y ) addM = liftM2 (+) Alberto Pardo, Marcos Viera Programación Funcional Mónada List - No Determinismo Computaciones que pueden retornar 0, 1 o más resultados. instance Monad [ ] where return x = [x ] m >>= f = concat (map f m) fail = [] Por ejemplo, sumar dos computaciones que pueden tener dos posibles valores: sumaL = do x ← [1, 2] y ← [3, 4] return (x + y ) Resulta en [4, 5, 5, 6] Alberto Pardo, Marcos Viera Programación Funcional Mónada Reader Computaciones que lee valores de un ambiente compartido. newtype Reader e a = Reader {runReader :: (e → a)} instance Monad (Reader e) where return a = Reader $ λe → a (Reader r ) >>= f = Reader $ λe → runReader (f (r e)) e ask :: Reader e e ask = Reader id local :: (e → e) → Reader e a → Reader e a local f c = Reader $ λe → runReader c (f e) Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Variables data Exp = · · · | Var ID | Let ID Exp Exp eval :: Exp → Reader (Map ID Int) Int eval (Num n) = return n eval (Add e e 0 ) = do a ← eval e b ← eval e 0 return (a + b) eval (Var v ) = do s ← ask return (fromJust $ lookup v s) eval (Let v e b) = do a ← eval e local (insert v a) (eval b) Alberto Pardo, Marcos Viera Programación Funcional Mónada de estado Computaciones que mantienen un estado newtype State s a = State {runState :: s → (a, s)} instance Monad (State s) where return a = State $ λs → (a, s) m >>= f = State $ λs → let (a, s 0 ) = runState m s in runState (f a) s 0 get = State $ λs → (s, s) put s = State $ \ → ((), s) Alberto Pardo, Marcos Viera Programación Funcional Evaluador con Estado data Exp = · · · | Var ID | Assign ID Exp eval :: Exp → State (Map ID Int) Int eval (Num n) = return n 0 eval (Add e e ) = do a ← eval e b ← eval e 0 return (a + b) eval (Var v ) = do s ← get return (fromJust $ lookup v s) eval (Assign v e) = do a ← eval e s ← get put (insert v a s) return a Alberto Pardo, Marcos Viera Programación Funcional