> Suppose I have the following class of monad transformers:
>
> class (Monad m, Monad (t m)) => MonadT t m where
> lift :: m a -> t m a
>
> and several instances of the class:
>
> instance MonadT MT1 where ...
> instance MonadT MT2 where ...
> . . .
> instance MonadT MTn where ...
>
> I obtain a new monad composing monad transformers through the IO monad:
>
> type MyMonad = MT1 (MT2 (... (MTn IO)))
>
> And now, if I want to lift "putStrLn" I must write:
>
> myPutStrLn = lift . lift . ... lift . putStrLn
>
> This works but looks ugly.
>
> The question is:
> Is there a way that the system could detect how many
> lifts are necessary to select the right function?
Yes, there is a way. It's a bit painful but it works. The idea is to
overload the IO primitives as well. This is in accordance with the
approach that a computational feature (here IO) is encapsulated in a
superclass of Monad.
> class (Monad io) => IOMonad io where
> putChar :: Char -> io ()
> putStr :: String -> io ()
> getChar :: io Char
> getLine :: io String
> getContents :: io String
> writeFile :: FilePath -> String -> io ()
> appendFile :: FilePath -> String -> io ()
> readFile :: FilePath -> io String
> fail :: IOError -> io a
> catch :: io a -> (IOError -> io a) -> io a
Now, you have to lift the operations through the various monad
transformers (typically using lift) and to make IO an instance of
IOMonad.
> instance IOMonad IO where
> putChar = Prelude.putChar
> putStr = Prelude.putStr
> getChar = Prelude.getChar
> getLine = Prelude.getLine
> getContents = Prelude.getContents
> writeFile = Prelude.writeFile
> appendFile = Prelude.appendFile
> readFile = Prelude.readFile
> fail = Prelude.fail
> catch = Prelude.catch
HTH Ralf