这个问题在这里已有答案:
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)
好的...到目前为止一切顺利。我们来试试一个Fractional参数。
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
实际上是什么类型”。