haskellfunctional-programmingmonadsmonad-transformers

Is this a generic transformer for any monad?


There have been claims that the IO monad has no monad transformer, or even cannot have a monad transformer. Why is there no IO transformer in Haskell?

First question: Then, what is this code? https://hackage.haskell.org/package/acme-iot-0.1.0.1/docs/Control-Monad-Trans-IO.html Is this not a monad transformer for IO, "adding IO capabilities to any monad" as the documentation says?

Second question: There is this blog post: http://h2.jaguarpaw.co.uk/posts/io-transformer/ where it is claimed that IOT m a = (forall x. m x -> IO x) -> IO a is a monad transformer for IO applied to an arbitrary foreign monad m. It is assumed, however, that the functions of type m x -> IO x must be monad morphisms. The blog post elaborates on this to show that (at least) the monad morphism laws hold for the map m a -> IOT m a.

Now, this construction seems to apply to arbitrary monads and seems to give a formula for a monad transformer for a completely arbitrary base monad Base a. The monad transformer BaseT is defined by:

 BaseT base m a = (forall x. m x -> base x) -> base a

Here the functions of type forall x. m x -> base x are assumed to be monad morphisms. (The code does not express that requirement in the type.)

Does this formula give a completely general monad transformer for any base monad base and any foreign monad m?

Are there monad morphisms lift: m a -> BaseT base m a and hoist :: (forall x. m x -> n x) -> BaseT base m a -> BaseT base n a?

Are there any other laws that a monad transformer must satisfy and BaseT does not satisfy?

One of my misgivings about BaseT is that the type forall x. m x -> base x could be void, for some m and base, even if we do not assume that it must be a monad morphism. There are some monads m and base for which there are simply no natural transformations of type forall x. m x -> base x, for example, m a = Maybe a and base a = a. So, in that case, the void type gets substituted into the formula and we get BaseT base m a = Void -> m a, which is equivalent to Unit. A monad transformer that is identically equal to Unit is useless for practical applications but does satisfy the monad morphism laws (as Unit is a lawful monad and any other monad can be mapped to Unit by a monad morphism). So, BaseT base m may not give a useful answer for some base and m. Does it mean that it is nevertheless useful when base = IO?


Solution

  • acme-iot, as its name implies, is a joke package. It breaks purity. The following program prints when a value is forced (which would require unsafeInterleaveIO or unsafePerformIO).

    module Main where
    
    import Control.Monad.Trans.IO
    import Control.Monad.Trans.Class
    
    e :: IOT [] ()
    e = do
      x <- lift [1,2]
      sequenceIO [print (x :: Int)]
    
    main :: IO ()
    main = do
      _ <- runIOT e -- prints nothing
    
      [a,b] <- runIOT e  -- a, b :: ()  -- prints nothing
      b `seq` pure ()  -- prints 2  (even though  () `seq` pure ()   should be equal to  pure ())
      a `seq` pure ()  -- prints 1
    

    The notion of "a monad transformer for a particular monad" is not well-defined. One may be "a monad transformer t such that t Identity is isomorphic to IO", and the generic construction you point out does satisfy that definition. The myth that there is no "IO monad transformer" comes from a mix of (1) if you make strong enough requirements for what that even means it may indeed not exist, (2) when it does exist, there may be no use case for such a thing.

    Another generic construction that may be relevant is the free monad transformer. FreeT IO is a monad transformer and it has IO in it.