消失约束的情况:更高等级类型的奇数

时间:2016-10-26 22:39:56

标签: haskell types type-constraints higher-rank-types

下面描述的所有实验都是用GHC 8.0.1完成的。

这个问题是对RankNTypes with type aliases confusion的跟进。这个问题归结为像这样的函数类型......

{-# LANGUAGE RankNTypes #-}

sleight1 :: a -> (Num a => [a]) -> a
sleight1 x (y:_) = x + y

...被类型检查器拒绝...

ThinAir.hs:4:13: error:
    * No instance for (Num a) arising from a pattern
      Possible fix:
        add (Num a) to the context of
          the type signature for:
            sleight1 :: a -> (Num a => [a]) -> a
    * In the pattern: y : _
      In an equation for `sleight1': sleight1 x (y : _) = x + y

...因为排名较高的约束Num a cannot be moved outside of the type of the second argument(如果我们改为a -> a -> (Num a => [a])则可能)。既然如此,我们最终会尝试为已经量化的变量添加更高级别的约束,即:

sleight1 :: forall a. a -> (Num a => [a]) -> a

完成这个重演后,我们可能会尝试简化一下这个例子。让我们用不需要(+)的东西替换Num,并将有问题的参数的类型与结果的类型分开:

sleight2 :: a -> (Num b => b) -> a
sleight2 x y = const x y

这不像之前那样有效(除了错误信息稍有变化):

ThinAir.hs:7:24: error:
    * No instance for (Num b) arising from a use of `y'
      Possible fix:
        add (Num b) to the context of
          the type signature for:
            sleight2 :: a -> (Num b => b) -> a
    * In the second argument of `const', namely `y'
      In the expression: const x y
      In an equation for `sleight2': sleight2 x y = const x y
Failed, modules loaded: none.

然而,在这里使用const可能是不必要的,所以我们可能会尝试自己编写实现:

sleight3 :: a -> (Num b => b) -> a
sleight3 x y = x

令人惊讶的是,这确实有效!

Prelude> :r
[1 of 1] Compiling Main             ( ThinAir.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t sleight3
sleight3 :: a -> (Num b => b) -> a
*Main> sleight3 1 2
1

更奇怪的是,第二个论点似乎没有实际Num约束:

*Main> sleight3 1 "wat"
1

我不太确定如何让它变得清晰可辨。或许我们可以这样说,就像我们可以在我们永远不会评估它的时候一样玩undefined,只要不在右手的任何地方用于统一,一个不可满足的约束就可以保持在一个类型中。侧。然而,这感觉就像一个相当弱的类比,特别是考虑到我们通常理解的非严格性是一个涉及价值观而非类型的概念。此外,这让我们无法理解世界String如何与Num b => b结合 - 假设这样的事情真的发生了,我完全不确定。那么,当一个约束似乎以这种方式消失时,对于正在发生的事情的准确描述是什么呢?

1 个答案:

答案 0 :(得分:13)

哦,它变得更奇怪了:

Prelude> sleight3 1 ("wat"+"man")
1
Prelude Data.Void> sleight3 1 (37 :: Void)
1

请参阅 对该参数的实际Num约束。只是,因为(正如chi已经评论过)b处于协变位置,这不是您在调用sleight3时必须提供的约束。相反,您可以选择任何类型b,然后无论它是什么,sleight3都会为它提供Num个实例!

嗯,显然这是假的。 sleight3 不能为字符串提供这样的num实例,绝大多数都不适用于Void。但它实际上 回想一下,约束多态值本质上只是字典参数的一个函数。 sleight3只是承诺在实际使用y之前提供这样的字典,但是它不会以任何方式使用y,所以它很好

它与这样的函数基本相同:

defiant :: (Void -> Int) -> String
defiant f = "Haha"

同样,参数函数显然不可能产生Int,因为没有Void值来评估它。但这也不需要,因为f被忽略了!

相比之下,sleight2 x y = const x y确实有点使用yconst的第二个参数只是一个rank-0类型,因此编译器需要在此时解析任何所需的字典。即使const最终也会抛出y,它仍然“强制”足够的这个值,以明显它没有很好地输入。