
On Tue, Oct 01, 2013 at 03:17:40PM +0300, Yitzchak Gale wrote:
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.)
I think that law follows automatically from parametricity, doesn't it?
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?
In tackling such questions I think it would be useful to know how many such instances there can be. Can there be more than one morphism between two monads? Between two applicatives? I would guess there are plenty of examples of functors with more than one functor morphism (natural transformation) between them. Perhaps these questions are easy, but I don't know how to approach them. Tom