簡體   English   中英

GHC Haskell目前的約束系統有什么問題?

[英]What's wrong with GHC Haskell's current constraint system?

我聽說Haskell的“破壞”約束系統存在一些問題,如GHC 7.6及以下版本。 它出什么問題了? 是否有可比的現有系統克服了這些缺陷?

例如,edwardk和tekmo都遇到了麻煩(例如來自tekmo的評論 )。

好的,我在發布之前與其他人進行了幾次討論,因為我想要做到這一點。 他們都告訴我,我描述的所有問題都歸結為缺乏多態約束。

這個問題最簡單的例子是MonadPlus類,定義如下:

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

......遵守以下法律:

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禁止我們編寫表單的約束:

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

因此,Haskell程序員必須通過定義一個單獨的類來處理這種特定的多態情況,從而解決類型系統的這個缺陷。

但是,這並不總是可行的解決方案。 例如,在我自己的pipes庫工作中,我經常遇到需要構造表單的約束:

(forall a' a b' b . Monad (p a a' b' b m)) => ...

不像MonadPlus的解決方案,我不能到交換機Monad型類不同類型的類來解決多態約束問題,因為后來我庫的用戶將失去do記號,這是一個很高的代價。

在編寫變換器時,也會出現這種情況,包括monad變換器和我在庫中包含的代理變換器。 我們想寫一些類似的東西:

data Compose t1 t2 m r = 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 (t m)) => MonadTrans t where
    lift :: (Monad m) => m r -> t m r

......但是Haskell的約束系統不允許這樣做。

隨着Haskell用戶轉向更高類型的構造函數,這個問題將變得越來越明顯。 您通常會有一個表單類型:

class SomeClass someHigherKindedTypeConstructor where
    ...

...但是你想要約束一些低級的派生類型構造函數:

class (SomeConstraint (someHigherKindedTypeConstructor a b c))
    => SomeClass someHigherKindedTypeConstructor where
    ...

但是,如果沒有多態約束,則該約束不合法。 我最近一直抱怨這個問題,因為我的pipes庫使用的類型非常多,所以我經常遇到這個問題。

有一些使用數據類型的解決方法,有幾個人向我提出,但我還沒有時間來評估它們以了解它們需要哪些擴展或哪一個正確地解決了我的問題。 更熟悉這個問題的人可能會提供一個單獨的答案,詳細說明解決方案及其工作原理。

[Gabriel Gonzalez回答的后續行動]

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

...

可以省略種類,以及排名1類型的forall

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

例如:

{-# 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

或者沒有Rank2Types (因為我們這里只有rank-1類型),並且使用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

“問題”是我們不能寫

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

要么

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

也就是說, forall m a. (Monad m, Monoid (ma)) forall m a. (Monad m, Monoid (ma))可以用作獨立約束,但不能使用*->*類型的新的單參數類型類別別名。

這是因為類型類定義機制的工作原理如下:

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

rhs側引入類型變量,而不是lhs或lhs或forall

相反,我們需要編寫2參數類型類:

{-# 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

缺點:我們每次使用MonadPlus都需要指定第二個參數。

問題:如何

instance Monoid a => MonadPlus Maybe a

如果MonadPlus是單參數類型類,可以寫嗎? MonadPlus Maybe來自base

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)如果我們想要*類型,(7 - 2 * 1) - 參數類型為* -> *類型,則產生(7 - 2 * 2) - 參數類型類,( 7 - 2 * 0)表示* -> * -> *類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM