解决不定式多态性的方法

时间:2018-11-29 04:20:59

标签: haskell polymorphism impredicativetypes

我是Haskell中一些较复杂的类型构造的新手,并且一直在搞乱。我目前在尝试获取我认为应该可以用于键入check的功能。

以下面的代码为例:

{-# LANGUAGE RankNTypes   #-}
{-# LANGUAGE TypeFamilies #-}

class X a where
  data Y a
  z :: Y a -> Int

data D1 = D1
instance X D1 where
  data Y D1 = YD1
  z _ = 1

data D2 = D2
instance X D2 where
  data Y D2 = YD2
  z _ = 2

sumZ :: X a => [Y a] -> Int
sumZ = foldl' sumFn 0
  where sumFn = flip ((+) . z)

我希望a = sumZ [YD1, YD2]键入check。这(显然)不起作用,因为a类型的变量被第一个YD1固定了。

我了解得足够多,知道我可能应该在这里使用排名较高的类型,所以我尝试了这一点:

sumZ' :: [(forall a. X a => Y a)] -> Int
sumZ' = foldl' sumFn 0
  where sumFn = flip ((+) . z)

但是,当我尝试对其进行编译时,我遇到了“强制性多态性”:

     • Illegal polymorphic type: forall a. X a => Y a
       GHC doesn't yet support impredicative polymorphism
     • In the type signature: sumZ' :: [(forall a. X a => Y a)] -> Int
    |
 48 | sumZ' :: [(forall a. X a => Y a)] -> Int
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

经过一番阅读之后,我发现常规的解决方案是将值包装在newtype中以绕过强制性多态性。

newtype Wrap = Wrap { unWrap :: forall a. X a => Y a }

sumZ'' :: [Wrap] -> Int
sumZ'' = foldl' sumFn 0
  where
    sumFn acc (Wrap v) = acc + (z v)

不幸的是,这似乎也不起作用。编译失败,并显示以下消息:

     • Ambiguous type variable ‘a0’ arising from a use of ‘z’
       prevents the constraint ‘(X a0)’ from being solved.
       Probable fix: use a type annotation to specify what ‘a0’ should be.
       These potential instances exist:
         instance X D1
           -- Defined at ...
         instance X D2
           -- Defined at ...
     • In the second argument of ‘(+)’, namely ‘(z v)’
       In the expression: acc + (z v)
       In an equation for ‘sumFn’: sumFn acc (Wrap v) = acc + (z v)
    |
 64 |     sumFn acc (Wrap v) = acc + (z v)
    |                                 ^^^

最后,我的问题:

  1. 为什么在这种情况下标准的“包装”技术不起作用?在检查v的类型时,我发现它的类型为forall a. X a => Y a,对我来说似乎应该可以使用。
  2. 如何使a = sumZ [YD1, YD2]工作?我要解决这个错误吗?

1 个答案:

答案 0 :(得分:4)

v :: forall a. X a => Y a就是为什么应用z无效的事实。

其中的forall意味着v可以是任何类型,您可以选择哪种类型。为了进行说明,请对此进行比较:

empty :: forall a. [a]
empty = []

empty可以是任何类型,消费者可以选择哪种类型。这就是forall在这里的意思。我希望这很明显。

您的值v也是如此:它可以是任何类型,您可以选择哪种类型。但是在您的代码中,您没有选择一种类型:您正在应用z,它本身可以与任何类型一起使用,因此v的类型仍未选择。这正是编译器在抱怨“模糊类型变量a0”时告诉您的。

要执行此操作,应将forall 放在Wrap 的另一侧:

data Wrap = forall a. X a => Wrap (Y a)

(您需要启用GADTs扩展名才能允许此操作)

这样,构造Wrap的任何人都必须选择特定的类型a。另一方面,当您进行模式匹配时,您会得到a类型,该类型由构造该值的人选择。