Haskell中的表观类型变量行为不连贯澄清

披露:我是一名学习 Haskell 的 Scala 程序员。

我想更好地理解类型变量的行为。

与 Scala 相反,我们似乎可以声明泛型类型的变量(即类型变量)。即泛型不限于功能。但是,我无法理解以下弃用情况,并希望对此进行一些解释。

为什么下面的代码对函数起作用:

g :: a -> a -- a can take the concrete type Num a => a or something else like Bool
g x = x

g 10
--g 10 :: Num a => a

g True
--g True :: Bool

然而,以下奇怪的事情发生在变量而不是函数上

e :: a
e = 1
--  No instance for (Num a) arising from the literal ‘1’
--      Possible fix:
--      add (Num a) to the context of
--        the type signature for:
--          e1 :: forall a. a
--    In the expression: 1
--    In an equation for ‘e1’: e1 = 1

当然,如果我在 REPL 中输入以下内容

e :: a; e = 1

Q1)从那看来,我们在变量和函数之间的泛型类型(即类型变量)的行为似乎有所不同?这是为什么 ?或者我还没有看到或得到什么?

现在奇怪的部分

如果在 Repl 但是我做

 e :: a; e = undefined

进而

e = 1
e = 1

然后我得到

:t e
-- e1 :: Num p => p

因此,“至少”在 REPL 中使用 undefined ,我得到了函数上泛型类型的行为。

Q2) 这里到底发生了什么?这一切对我来说听起来不太连贯。谁能解释一下?

回答

Haskell 中的类型变量是普遍量化的。这意味着实际的类型g

g :: ? a . a -> a

量词通常是隐式的,但forall如果您启用某些 GHC 扩展,您实际上可以在 Haskell 源代码中将其拼写出来(如)。

这是什么意思?嗯,从字面上看,g具有a -> a 所有可能值的类型a。也就是说,g是一个Bool->Boll和一个String->String和一个Int->Int函数。它具有所有这些类型。

当您编写 时g x = x,您并没有违反 的类型给出的承诺g,即参数可以是任何类型并且结果必须是相同类型。事实上,结果是x,参数也是x,并且x显然与 具有相同的类型x

然而,如果你写g x = x + 42,这违背了承诺。x不能是类型BoolString更多。所以编译器会抱怨。

现在,记号

e :: a

以非常相似的方式应该被解释为

e :: ? a . a

这意味着e具有a 的所有可能值的类型a。也就是说,它具有所有可能的类型:IntandStringBoolandInt->Int以及无限多的其他类型。

您可以通过定义e为来满足类型

e = undefined

(实际上,这基本上是您满足它的唯一方法)。但是,如果你写

你违反了e. 所以你会得到和上面一样的错误。

如果你写e = undefined和 after e = 1,那么在 REPL 中,第二个定义会影响第一个定义,所以你有两个不同的变量命名为e,每个变量都有自己的类型。如果您正在使用 REPL,您可以随时重新定义任何名称,这就是 REPL 的用途!这在普通的 Haskell 模块中当然是不可能的。


以上是Haskell中的表观类型变量行为不连贯澄清的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>