约束约束

时间:2017-05-07 02:55:49

标签: haskell ghc constraint-kinds

我可以写下以下内容:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ConstraintKinds #-}

f :: Integral a => (forall b. Num b => b) -> a
f = id

一切都很好。据推测,GHC可以从Integral派生Num,所以一切都很顺利。

我可能有点慌张,但我仍然很好:

class Integral x => MyIntegral x
instance Integral x => MyIntegral x

class Num x => MyNum x
instance Num x => MyNum x

f' :: MyIntegral a => (forall b. MyNum b => b) -> a
f' = id

所以我想说这是概括的,就像这样:

g :: c2 a => (forall b. c1 b => b) -> a
g = id

现在显然这会吐出假人,因为GHC无法从c2派生c1,因为c2没有约束。

我需要在g的类型签名中添加什么来表示“您可以从c2派生c1”?

1 个答案:

答案 0 :(得分:7)

constraints包通过其:-(" entails")类型提供此问题的解决方案:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}

import GHC.Exts

data Dict :: Constraint -> * where
    Dict :: a => Dict a

newtype a :- b = Sub (a => Dict b)
infixr 9 :-

g, g' :: c2 a => c2 a :- c1 a -> (forall b. c1 b => b) -> a
g (Sub Dict) x = x

然后,通过传递适当的证人,我们可以恢复原来的例子:

integralImpliesNum :: Integral a :- Num a
integralImpliesNum = Sub Dict

f :: Integral a => (forall b. Num b => b) -> a
f = g integralImpliesNum

事实上,此g仅仅是\\运算符的翻转和专用版本:

(\\) :: a => (b => r) -> (a :- b) -> r
r \\ Sub Dict = r
infixl 1 \\

g' = flip (\\)

如果你有时间,爱德华·凯梅特的演讲"Type Classes vs the World"是对这一切如何运作的一个很好的介绍。