GHC Haskell目前的约束系统有什么问题?

我听说Haskell的“破坏”约束系统存在一些问题,如GHC 7.6及以下。 它出什么问题了? 是否有一个可比的现有系统克服这些缺陷?

例如,edwardk和tekmo都遇到了麻烦(例如tekmo的这个评论 )。

好的,我之前和其他人进行了几次讨论,因为我希望得到这个结果。 他们都告诉我,我所描述的所有问题都归结为缺乏多态约束。

这个问题的最简单的例子是MonadPlus类,定义为:

 class MonadPlus m where mzero :: ma mplus :: ma -> ma -> ma 

…遵循以下法律:

 mzero `mplus` m = m m `mplus` mzero = m (m1 `mplus` m2) `mplus` m3 = m1 `mplus` (m2 `mplus` m3) 

请注意,这些是Monoid法则,其中Monoid类由以下给出:

 class Monoid a where mempty :: a mappend :: a -> a -> a mempty `mplus` a = a a `mplus` mempty = a (a1 `mplus` a2) `mplus` a3 = a1 `mplus` (a2 `mplus` a3) 

那么为什么我们甚至有MonadPlus类? 原因是因为Haskell禁止我们写这个forms的约束:

 (forall a . Monoid (ma)) => ... 

所以Haskell程序员必须通过定义一个单独的类来处理这个特定的多态情况,来解决types系统的这个缺陷。

但是,这并不总是一个可行的解决scheme。 例如,在我自己的pipes库工作中,我经常遇到需要对表单进行约束:

 (forall a' ab' b . Monad (paa' b' bm)) => ... 

MonadPlus解决scheme不同,我不能将Monadtypes类切换到不同的types类来解决多态约束问题,因为那样我的图书馆用户就会丢失标记,这是一个很高的代价。

这也出现在组成变压器时,包括monad变压器和我在图书馆中包含的代理变压器。 我们想写一些类似于:

 data Compose t1 t2 mr = C (t1 (t2 m) r) instance (MonadTrans t1, MonadTrans t2) => MonadTrans (Compose t1 t2) where lift = C . lift . lift 

这第一次尝试不起作用,因为lift不会限制其结果是Monad 。 我们实际上需要:

 class (forall m . Monad m => Monad (tm)) => MonadTrans t where lift :: (Monad m) => mr -> tmr 

…但是Haskell的约束系统不允许这样做。

随着Haskell用户移动到更高types的构造函数,这个问题会变得越来越明显。 你通常会有一个表单的types:

 class SomeClass someHigherKindedTypeConstructor where ... 

…但你会想约束一些较低的派生types的构造函数:

 class (SomeConstraint (someHigherKindedTypeConstructor abc)) => SomeClass someHigherKindedTypeConstructor where ... 

但是,没有多态约束,这个约束是不合法的。 我一直是最近抱怨这个问题的人,因为我的pipes库使用的types非常多,所以我经常遇到这个问题。

有一些使用数据types的解决方法是几个人向我提出的,但是我还没有时间来评估它们,以了解它们需要哪些扩展或哪些扩展正确地解决了我的问题。 有人更熟悉这个问题,也许可以提供一个单独的答案,详细解决这个问题,为什么它的作品。

[加布里埃尔·冈萨雷斯回答的后续]

Haskell中约束和量化的正确表示法如下:

 <functions-definition> ::= <functions> :: <quantified-type-expression> <quantified-type-expression> ::= forall <type-variables-with-kinds> . (<constraints>) => <type-expression> <type-expression> ::= <type-expression> -> <quantified-type-expression> | ... ... 

种类可以省略,也可以省略一类:

 <simply-quantified-type-expression> ::= (<constraints-that-uses-rank-1-type-variables>) => <type-expression> 

例如:

 {-# LANGUAGE Rank2Types #-} msum :: forall m a. Monoid (ma) => [ma] -> ma msum = mconcat mfilter :: forall m a. (Monad m, Monoid (ma)) => (a -> Bool) -> ma -> ma mfilter p ma = do { a <- ma; if pa then return a else mempty } guard :: forall m. (Monad m, Monoid (m ())) => Bool -> m () guard True = return () guard False = mempty 

或者没有Rank2Types (因为我们在这里只有排名第一的types),并且使用CPP (j4f):

 {-# LANGUAGE CPP #-} #define MonadPlus(m, a) (Monad m, Monoid (ma)) msum :: MonadPlus(m, a) => [ma] -> ma msum = mconcat mfilter :: MonadPlus(m, a) => (a -> Bool) -> ma -> ma mfilter p ma = do { a <- ma; if pa then return a else mempty } guard :: MonadPlus(m, ()) => Bool -> m () guard True = return () guard False = mempty 

“问题”是我们不能写

 class (Monad m, Monoid (ma)) => MonadPlus m where ... 

要么

 class forall m a. (Monad m, Monoid (ma)) => MonadPlus m where ... 

也就是说, forall m a. (Monad m, Monoid (ma))可以用作独立约束,但是不能用*->*types的新的单参数types类别进行别名。

这是因为types定义机制是这样工作的:

 class (constraints[a, b, c, d, e, ...]) => ClassName (abc) (de) ... 

rhs侧引入typesvariables,而不是lhs或lhs。

相反,我们需要编写2参数types类:

 {-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-} class (Monad m, Monoid (ma)) => MonadPlus ma where mzero :: ma mzero = mempty mplus :: ma -> ma -> ma mplus = mappend instance MonadPlus [] a instance Monoid a => MonadPlus Maybe a msum :: MonadPlus ma => [ma] -> ma msum = mconcat mfilter :: MonadPlus ma => (a -> Bool) -> ma -> ma mfilter p ma = do { a <- ma; if pa then return a else mzero } guard :: MonadPlus m () => Bool -> m () guard True = return () guard False = mzero 

缺点:我们每次使用MonadPlus都需要指定第二个参数。

问题:如何

 instance Monoid a => MonadPlus Maybe a 

如果MonadPlus是单参数typestypes,可以写入? MonadPlus Maybebase

 instance MonadPlus Maybe where mzero = Nothing Nothing `mplus` ys = ys xs `mplus` _ys = xs 

作品不像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] 

forall mabncd e. (Foo (mab), Bar (ncd) e) ,所有的forall mabncd e. (Foo (mab), Bar (ncd) e) forall mabncd e. (Foo (mab), Bar (ncd) e)如果我们需要*types,(7 – 2 * 1) – 参数typestypes为* -> *types, 7 – 2 * 0)为* -> * -> *types。