{-# LANGUAGE TypeOperators, TypeSynonymInstances #-} import Control.Monad -- Para componer el functor [] con el functor Maybe -- podemos hacer una version de fmap compuesta compfmap :: (a -> b) -> [ Maybe a ] -> [ Maybe b ] compfmap f v = fmap (fmap f) v -- Si queremos hacerlo de forma generica para cualquier par de functores f y g -- definimos un constructor de tipos para la composicion newtype Compose f g a = Compose { getCompose :: f (g a) } deriving Show type f :. g = Compose f g infixr :. -- Entonces podemos definir fmapCompose :: (a -> b) -> ([ ] :. Maybe) a -> ([] :. Maybe) b fmapCompose f (Compose v) = Compose $ fmap (fmap f) v -- En general, la instancia de Functor es: instance (Functor f, Functor g) => Functor (f :. g) where fmap f (Compose x) = Compose (fmap (fmap f) x) -- Por lo que la composicion de dos functores es un functor ej1 :: ([] :. Maybe) Int ej1 = fmap (+ 4) $ Compose [Just 1, Nothing, Just 5] -- De forma similar con functores aplicativos compapp :: [ Maybe (a -> b) ] -> [ Maybe a ] -> [ Maybe b ] compapp f v = pure (<*>) <*> f <*> v ej2 = [Just (+2), Nothing, Just (+9)] `compapp` [Just 5, Just 8] -- la composicion es generica appCompose :: (Applicative f, Applicative g) => (f :. g) (a -> b) -> (f :. g) a -> (f :. g) b appCompose (Compose f) (Compose v) = Compose $ (<*>) <$> f <*> v ej3 = (Compose [Just (+2), Nothing, Just (+9)]) `appCompose` (Compose [Just 5, Just 8]) -- Por lo que podemos definir la instancia de Applicative: instance (Applicative f, Applicative g) => Applicative (Compose f g) where pure x = Compose (pure (pure x)) Compose f <*> Compose x = Compose $ (<*>) <$> f <*> x ej4 = (Compose [Just (+2), Nothing, Just (+9)]) <*> (Compose [Just 5, Just 8]) -- Entonces la composicion de dos functores aplicativos es un functor aplicativo -- Que sucede con las monadas? -- Podemos definir un bind para la composicion de [] y Maybe -- pero utiliza funciones especificas de una de las monadas compbind :: [ Maybe a ] -> (a -> [ Maybe b ]) -> [ Maybe b ] compbind lm f = lm >>= maybe (return Nothing) f -- Si quisiera hacer algo generico para cualquier par de monadas bindCompose :: (Monad m, Monad n) => m (n a) -> (a -> m (n b)) -> m (n b) bindCompose lm f = lm >>= \m -> let nmnt = m >>= \a -> return $ f a in fmap join (swap nmnt) -- No lo podria aplicar a cualquier par de monadas, porque -- necesito tener una forma de poder 'swappear' los efectos swap ::n (m t) -> m (n t) swap = undefined -- Por lo que las monadas no se pueden componer en una monada -- (aunque si en un functor aplicativo). -- Para poder combinar efectos modelados con monadas puedo -- usar transformadores de monadas.