Skip to content

Instantly share code, notes, and snippets.

@levinotik
Created August 3, 2015 01:05
Show Gist options
  • Save levinotik/5c5e1ee2554972a5cb74 to your computer and use it in GitHub Desktop.
Save levinotik/5c5e1ee2554972a5cb74 to your computer and use it in GitHub Desktop.
module TransformersPart1 where
import Control.Monad.Trans.Maybe
import Data.Functor
import Text.Read (readMaybe)
-- plain old Maybe example (avoiding division by zero)
ex1 :: Int -> Int -> Maybe Int
ex1 x y | y > 0 = Just (x `div` y)
| otherwise = Nothing
-- now we have two Monad layers. Maybe and IO.
ex2 :: IO (Maybe Int)
ex2 = readMaybe <$> getLine
-- using the previous example, we now want to map the Int to a String
-- we fmap twice; once on the IO and then on the Maybe.
ex3 :: IO (Maybe String)
ex3 = fmap (fmap show) ex2
-- we can actually do this for any two functors though
-- because functors compose
ex4 :: (Functor f, Functor g) => (a -> b) -> f (g a) -> f (g b)
ex4 f a = fmap (fmap f) a
-- but when we try to bind we have issues
-- we can do it if we know about one of the Monads.
ex5 :: Monad m => m (Maybe a) -> (a -> m (Maybe b)) -> m (Maybe b)
ex5 a f = a >>= (maybe (return Nothing) f)
-- but you can't write bind for any two arbitrary monads
ex6 :: (Monad m, Monad n) => m (n a) -> (a -> m (n b)) -> m (n b)
ex6 = error "try implementing it"
-- With transformers, you have a monad instance for the transformer
-- where there's a concrete monad and any other monad on top. Hence
-- Monad transformers such as MaybeT, EitherT, etc. The monad instance has to
-- be defined with regard to some known, conrete monad + an arbitrary monad.
-- the type is `newtype MaybeT m a` so we can use `ex2` to construct one
ex7 :: MaybeT IO Int
ex7 = MaybeT $ ex2
-- the monad instance for MaybeT lets us to a single bind
ex8 :: MaybeT IO Int
ex8 = do
x <- ex7
return (x + 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment