I\'ve heard that there are some problems with Haskell\'s \"broken\" constraint system, as of GHC 7.6 and below. What\'s \"wrong\" with it? Is there a comparable existing system
[a follow-up to Gabriel Gonzalez answer]
The right notation for constraints and quantifications in Haskell is the following:
::= ::
::= forall . () =>
::= ->
| ...
...
Kinds can be omitted, as well as forall
s for rank-1 types:
::= () =>
For example:
{-# LANGUAGE Rank2Types #-}
msum :: forall m a. Monoid (m a) => [m a] -> m a
msum = mconcat
mfilter :: forall m a. (Monad m, Monoid (m a)) => (a -> Bool) -> m a -> m a
mfilter p ma = do { a <- ma; if p a then return a else mempty }
guard :: forall m. (Monad m, Monoid (m ())) => Bool -> m ()
guard True = return ()
guard False = mempty
or without Rank2Types
(since we only have rank-1 types here), and using CPP
(j4f):
{-# LANGUAGE CPP #-}
#define MonadPlus(m, a) (Monad m, Monoid (m a))
msum :: MonadPlus(m, a) => [m a] -> m a
msum = mconcat
mfilter :: MonadPlus(m, a) => (a -> Bool) -> m a -> m a
mfilter p ma = do { a <- ma; if p a then return a else mempty }
guard :: MonadPlus(m, ()) => Bool -> m ()
guard True = return ()
guard False = mempty
The "problem" is that we can't write
class (Monad m, Monoid (m a)) => MonadPlus m where
...
or
class forall m a. (Monad m, Monoid (m a)) => MonadPlus m where
...
That is, forall m a. (Monad m, Monoid (m a))
can be used as a standalone constraint, but can't be aliased with a new one-parametric typeclass for *->*
types.
This is because the typeclass defintion mechanism works like this:
class (constraints[a, b, c, d, e, ...]) => ClassName (a b c) (d e) ...
i.e. the rhs side introduce type variables, not the lhs or forall
at the lhs.
Instead, we need to write 2-parametric typeclass:
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
class (Monad m, Monoid (m a)) => MonadPlus m a where
mzero :: m a
mzero = mempty
mplus :: m a -> m a -> m a
mplus = mappend
instance MonadPlus [] a
instance Monoid a => MonadPlus Maybe a
msum :: MonadPlus m a => [m a] -> m a
msum = mconcat
mfilter :: MonadPlus m a => (a -> Bool) -> m a -> m a
mfilter p ma = do { a <- ma; if p a then return a else mzero }
guard :: MonadPlus m () => Bool -> m ()
guard True = return ()
guard False = mzero
Cons: we need to specify second parameter every time we use MonadPlus
.
Question: how
instance Monoid a => MonadPlus Maybe a
can be written if MonadPlus
is one-parametric typeclass? MonadPlus Maybe
from base
:
instance MonadPlus Maybe where
mzero = Nothing
Nothing `mplus` ys = ys
xs `mplus` _ys = xs
works not like Monoid Maybe
:
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
Nothing `mappend` m = m
m `mappend` Nothing = m
Just m1 `mappend` Just m2 = Just (m1 `mappend` m2) -- < here
:
(Just [1,2] `mplus` Just [3,4]) `mplus` Just [5,6] => Just [1,2]
(Just [1,2] `mappend` Just [3,4]) `mappend` Just [5,6] => Just [1,2,3,4,5,6]
Analogically, forall m a b n c d e. (Foo (m a b), Bar (n c d) e)
gives rise for (7 - 2 * 2)-parametric typeclass if we want *
types, (7 - 2 * 1)-parametric typeclass for * -> *
types, and (7 - 2 * 0) for * -> * -> *
types.