对于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的类型的变量。但是,分配失败。
我确定这是我的一些基本误解。可能是同一件事使第一个示例无法正常工作。我真的很想弄清楚它,然后再开始犯更严重的概念错误。
答案 0 :(得分:9)
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
是所有类型的值(使用时可能需要的任何类型)”。因此,可以归结为“存在”还是“全部”,或者归结为“谁选择a
类型实际上是什么类型。”