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



对Haskell来说非常陌生,试图理解类型类和变量如何交互。

我首先要玩的是:

i :: a; i = 1

我的期望是,由于我被尽可能通用地键入,我应该能够绝对地为它分配任何东西。 (我知道我可能无法对变量 i 做任何事情,但这并不重要。

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

i :: Num a => a; i = 1

在玩了一会儿之后,我想出了以下内容:

g :: Num a => a -> a; g a = a + 1
g 1
(returned 2)
gg :: Num a => a; gg = g 1
gg
(returned 2)

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

g :: Num a => a -> a; g a = a + 1
g 1.3
(returned 2.3)
gg :: Num a => a; gg = g 1.3
(error)

所以,请...导致这种情况的变量是什么? 从非函数式编程背景来看,它"看起来"就像我有一个函数,该函数返回一个实现 Num 类型的值,并尝试将其分配给具有实现 Num 类型的变量。 然而,分配失败了。

我敢肯定这是我的一些基本误解。 这可能是阻止第一个示例工作的原因。我真的很想在我开始犯更严重的概念错误之前把它理顺。

i :: a; i = 1

我的期望是,由于我被尽可能通用地键入,我应该能够绝对地为它分配任何东西。(我知道我可能无法对变量 i 做任何事情,但这并不重要。

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

(顺便说一下,我们通常称i = 1为"绑定"或"定义",而不是"赋值",因为这意味着我们以后可以重新赋值。

gg :: Num a => a; gg = g 1.3
(error)

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

用户可以使用显式签名(print (gg :: Int))来选择类型,或者将其置于"强制"类型的上下文中(print (length "hello" + gg)强制Int,因为length返回Int)。

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

-- Haskell
i :: a
i = 1            -- type error
-- pseudo-Java
<A> A getI() {
return 1;      -- type error
}

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

最新更新