在Haskell中对类型类和变量赋值的误解

Mar*_*len 4 haskell types variable-assignment typeclass parametric-polymorphism

对Haskell来说是非常新的知识,它试图了解类型类和变量如何交互。

我要玩的第一件事是:

i :: a; i = 1
Run Code Online (Sandbox Code Playgroud)

我的期望是,由于我输入的字词尽可能通用,因此我应该绝对可以为其分配任何内容。(我知道我可能无法对变量i做任何事情,但这并不重要。)

但是我错了。上面给出了一个错误,并要求它是:

i :: Num a => a; i = 1
Run Code Online (Sandbox Code Playgroud)

经过多玩之后,我想到了以下内容:

g :: Num a => a -> a; g a = a + 1
g 1
(returned 2)
gg :: Num a => a; gg = g 1
gg
(returned 2)
Run Code Online (Sandbox Code Playgroud)

好的...到目前为止很好。让我们尝试一个小数参数。

g :: Num a => a -> a; g a = a + 1
g 1.3
(returned 2.3)
gg :: Num a => a; gg = g 1.3
(error)
Run Code Online (Sandbox Code Playgroud)

那么,请...导致变量的原因是什么?从非函数式编程背景来看,它“看起来”像我有一个函数,该函数返回实现Num的类型的值,并尝试将其分配给实现Num的类型的变量。但是,分配失败。

我确信这是我的一些基本误解。可能是同一件事使第一个示例无法正常工作。我真的很想弄清楚它,然后再开始犯更严重的概念错误。

chi*_*chi 9

i :: a; i = 1
Run Code Online (Sandbox Code Playgroud)

我的期望是,由于我输入的字词尽可能通用,因此我应该绝对可以为其分配任何内容。(我知道我可能无法对变量i做任何事情,但这并不重要。)

不,这是另一回事。该类型表示以后可以使用该值的方式,即表示用户可以i假装该值当时可能是任何类型。本质上,用户选择实际的类型a,并且代码定义i :: a必须符合用户的任何此类选择。

(通过我们通常所说的i = 1“绑定”或“定义”,而不是“分配”的方式,因为这意味着我们以后可以重新分配。)

gg :: Num a => a; gg = g 1.3
(error)
Run Code Online (Sandbox Code Playgroud)

同样的原则在这里适用。gg声称是用户可能想要的任何数字类型,但是如果用户稍后选择,Int则定义g 1.3不适合Int

用户可以使用显式签名(print (gg :: Int))选择类型,也可以将其置于“强制”该类型的上下文中(自返回以来print (length "hello" + gg)强制)。IntlengthInt

如果您熟悉其他一些语言中的“泛型”,则可以使用以下代码进行比较:

-- Haskell
i :: a
i = 1            -- type error

-- pseudo-Java
<A> A getI() {
  return 1;      -- type error
}
Run Code Online (Sandbox Code Playgroud)

从更理论的角度来看,您正在考虑错误的量词。当您编写时i :: a,您正在考虑i :: exists a . a(不是真正的Haskell类型)读为“ i是某种类型的值(在定义时选择)”。相反,在Haskell中,其i :: a含义i :: forall a . a为“ i是所有类型的值(使用时可能需要的任何类型)”。因此,归结为“存在”与“全部”,或“谁选择实际的类型类型a”。