Search code examples
haskellmonadstypeclassdo-notation

Rebind do notation with typeclass-free monad


It is possible to rebind the (>>=) and return for a monad using explicit dictionary passing like this:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RebindableSyntax #-}

module Lib where

import Prelude hiding ((>>=), return)

data MonadDict m = MonadDict {
  bind :: forall a b. m a -> (a -> m b) -> m b ,
  ret :: forall a. a -> m a }

(>>=) :: (MonadDict m -> m a) -> (a -> (MonadDict m -> m b)) -> (MonadDict m -> m b)
return :: a -> (MonadDict m -> m a)

monadDictIO :: MonadDict IO

usage = let
  monadicCode = do
    ln <- const getLine 
    const . putStrLn $ ln
  in monadicCode monadDictIO

Is there a better way, how to represent the monad so one can avoid ignoring the MonadDict monad instance argument (using const) in every usage of the monadic action?


Solution

  • You can do like this:

    {-# LANGUAGE RankNTypes #-}
    {-# LANGUAGE RebindableSyntax #-}
    {-# LANGUAGE RecordWildCards #-}
    
    module Lib where
    import Prelude hiding(return, fail, (>>=), (>>))
    
    data MonadDict m = MonadDict
        { (>>=)  :: forall a b. m a -> (a -> m b) -> m b
        , (>>)   :: forall a b. m a -> m b -> m b
        , return :: forall a. a -> m a
        , fail   :: forall a. String -> m a
        }
    
    monadDictIO :: MonadDict IO
    monadDictIO = ...
    
    foo :: MonadDict m -> String -> m ()
    foo = ...
    
    usage = let
        monadicCode m@MonadDict{..} = do
            ln <- getLine
            putStrLn ln
            foo m ln
        in monadicCode monadDictIO