Yogesh Sajanikar
Yogesh Sajanikar

Reputation: 1116

Recover the do notation syntax

I was reading through Scrap your type classes. This provides an alternatives for type classes. However, I was stuck with a comment by Paul Chiusano which talked about recovering the do notation syntax.

Frankly I could not understand how

return :: a -> (Monad f -> f a) 
(>>=) :: (Monad f -> f a) -> (a -> (Monad f -> f b)) -> (Monad f -> f b)

would help recover do notation

You can implement all the monadic combinators like this, and desugar do notation to them. The do block evaluates to a function that accepts the monad dictionary, so you can even conveniently write code that's polymorphic in the choice of monad, without having to thread the dictionary around manually.

And especially, how it would fit int he context of GADT style approach mentioned in the above article?

Upvotes: 9

Views: 144

Answers (1)

user824425
user824425

Reputation:

{-# LANGUAGE Rank2Types, RebindableSyntax #-}

import qualified Prelude
import qualified System.IO
import Prelude (String, ($), (++))
import System.IO (IO)

With Gabriel Gonzales' suggestion

data MonadI f = MonadI {
    _return :: forall a . a -> f a,
    _bind :: forall a b . f a -> (a -> f b) -> f b
}

you could implement the necessary functions return and (>>=) as follows, with the types as suggested by Paul Chiusano:

return :: a -> (MonadI f -> f a)
return x = \dict -> (_return dict) x

(>>=) :: (MonadI f -> f a) -> (a -> (MonadI f -> f b)) -> (MonadI f -> f b)
ma >>= f = \dict -> (_bind dict) (ma dict) (\x -> f x dict)

This is not enough to recover do-notation, because you'll also need (>>) and (sadly) fail. You can implement them as follows:

(>>) :: (MonadI f -> f a) -> (MonadI f -> f b) -> (MonadI f -> f b)
ma >> mb = ma >>= (\_ -> mb)

fail :: String -> MonadI f -> f a
fail str = \_ -> Prelude.error str -- Because let's not further entertain that idea.

And now we have the required facilities to write simple programs:

main :: IO ()
main = (\m -> m monad'IO) $ do
    putStrLn "What is your name?"
    name <- getLine
    putStrLn $ "Hello, " ++ name

Of course, we're going to have to borrow some things from System.IO for that:

getLine :: MonadI IO -> IO String
getLine = \_ -> System.IO.getLine

putStrLn :: String -> (MonadI IO -> IO ())
putStrLn str = \_ -> System.IO.putStrLn str

monad'IO :: MonadI IO
monad'IO = MonadI {
    _return = (Prelude.return :: a -> IO a),
    _bind = ((Prelude.>>=) :: IO a -> (a -> IO b) -> IO b)
}

Upvotes: 6

Related Questions