What's wrong with GHC Haskell's current constraint system?

前端 未结 2 592
一向
一向 2021-02-01 01:33

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

2条回答
  •  天涯浪人
    2021-02-01 02:06

    [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 foralls 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.

提交回复
热议问题