
Tom Ellis wrote:
Shouldn't it be an *Applicative* constraint?
class Applicative t => ApplicativeIO t where liftIO :: IO a -> t a
and require that
liftIO (pure x) = pure x liftIO (f <*> x) = liftIO f <*> liftIO x
Seems like ApplicativeIO makes more sense than MonadIO, which is unnecessarily restrictive. With planned Functor/Applicative/Monad shuffle, the former could completely replace the latter.
In fact, it even makes sense to define it as FunctorIO, with the only laws being that liftIO commutes with fmap and preserves id, i.e., that it is a natural transformation. (Those laws are also needed for ApplicativeIO and MonadIO.) Since Haskell is not dependently typed and we specify laws only as human-readable comments, should we define only FunctorIO and then just specify in the comments the additional laws that should be satisfied for Applicative and Monad? Or should we have equivalent definitions that differ only in the laws that are expected to be satisfied? Or should the different definitions have different superclass constraints? Let's get it right this time and not create yet another Functor/Applicative/Monad mess. Thanks, Yitz