Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
与Haskell类的混淆_Haskell - Fatal编程技术网

与Haskell类的混淆

与Haskell类的混淆,haskell,Haskell,我对Haskell的课程感到困惑,如下所示 我可以定义一个接受整型参数的函数,并成功地为其提供Num参数: gi :: Integral a => a -> a gi i = i gin = gi (3 :: Num a => a) fn :: Num a => a -> a fn n = n fni = fn (3 :: Integral a => a) 我可以定义一个接受Num参数的函数,并成功地为其提供整型参数: gi :: Integral a =

我对Haskell的课程感到困惑,如下所示

我可以定义一个接受整型参数的函数,并成功地为其提供Num参数:

gi :: Integral a => a -> a
gi i = i
gin = gi (3 :: Num a => a)
fn :: Num a => a -> a
fn n = n
fni = fn (3 :: Integral a => a)
我可以定义一个接受Num参数的函数,并成功地为其提供整型参数:

gi :: Integral a => a -> a
gi i = i
gin = gi (3 :: Num a => a)
fn :: Num a => a -> a
fn n = n
fni = fn (3 :: Integral a => a)
我可以定义一个整数值并给它赋值

i :: Integral a => a
i = (3 :: Num a => a)
但是如果我试图定义一个Num值,那么如果我给它赋值,就会得到一个解析错误

- this doesn't work
n :: Num a => a
n = (3 :: Integral a => a)
也许我被我的OO背景搞糊涂了。但是,为什么函数变量似乎让你“双向”去做,即当一个超类是“预期的”时,可以提供一个子类的值,当一个子类是预期的时,可以提供一个超类的值,而在值赋值中,你可以为一个子类值提供一个超类,但不能为一个超类值赋值


作为比较,在OO编程中,您通常可以将子值分配给父类型,但反之亦然。在Haskell中,第二对例子的情况似乎正好相反。

前两个例子实际上与
Num
Integral
之间的关系没有任何关系

看看
gin
fni
的类型。让我们一起做:

> :t gin
gin :: Integer

> :t fni
fni :: Integer
发生什么事了?这称为“类型默认”

从技术上讲,Haskell中的任何数值文字,如
3
5
42
,都具有类型
Num a=>a
。因此,如果你想让它仅仅是一个整数,你必须总是写
42::integer
,而不是只写
42
。这很不方便

为了解决这个问题,Haskell有一些规则,在某些特殊情况下,当类型变成泛型时,指定要替换的具体类型。如果同时使用
Num
Integral
,则默认类型为
Integer

因此,当编译器看到
3
,并将其用作
gi
的参数时,编译器默认为
Integer
。就这样。您对
Num a
的附加约束没有进一步的影响,因为
Integer
实际上已经是
Num
的一个实例


另一方面,对于最后两个示例,区别在于您显式指定了类型签名。你不是让编译器来决定的,不是!你特别说过
n::Num a=>a
。因此编译器不能再决定
n::Integer
。它必须是通用的

由于它是泛型的,并且被限制为
Num
Integral
类型不起作用,因为正如您正确指出的,
Num
不是
Integral
的子类

您可以通过给
fni
一个类型签名来验证这一点:

-- no longer works
fni :: Num a => a
fni = fn (3 :: Integral a => a)

等等,但是
n
是否仍然有效?毕竟,在OO中,这将很好地工作。以C#为例:

class Num{}
类积分:Num{}
类整数:整数{}
Num a=(整数)3
//^这是有效的(模伪码),因为'Integer'是'Num'的子类`
啊,但这不是一般类型!在上面的示例中,
a
是一个具体类型的值
Num
,而在Haskell代码中
a
本身就是一个类型,但被限制为
Num
。这更像是一个C#接口,而不是一个C#类

泛型类型(无论是否在Haskell中)实际上是以另一种方式工作的!取如下值:

x :: a
x = ...
这个类型签名的意思是“任何需要
x
的人,都来拿它!但是先说出一个类型
a
。然后
x
的值就是这个类型。无论你说出哪种类型,这就是
x

或者,更明确地说,选择泛型类型的是函数的调用方(或值的使用者),而不是实现者

因此,如果你说
n::Num a=>a
,这意味着值
n
必须能够“变形”为任何类型
a
,只要该类型有
Num
实例。无论谁将在计算中使用
n
,该人将选择
a
是什么。作为
n
的实现者,您不能选择这样做

既然你不能选择什么是
a
,你就不能把它缩小到不仅仅是一个
Num
,而是一个
积分。因为,你知道,有些
Num
s不是
Integral
s,所以如果使用
n
的人选择其中一种非
Integral
类型作为
a
,你会怎么做



i
的情况下,这很好,因为每个
Integral
也必须是
Num
,所以无论
i
的消费者为
a
选择什么,你肯定知道它将是
Num

你真的得到了一个解析错误,还是得到了一个表示
无法推断的错误(积分a)由上下文中的表达式类型签名产生:Num a
?当您创建
gin
fni
并使用
:t
检查它们的类型时,您看到了什么?