这是要记住的一个区别:
C# 有子类型,但 Haskell 没有,这意味着,一方面,您只需查看 Haskell 类型即可了解更多内容。
id :: a -> a
这个 Haskell 函数接受一个类型的值并返回相同类型的相同值。如果你给它 a Bool
,它会返回 a Bool
。给它一个Int
,它会返回一个Int
。给它一个Person
,它会返回一个Person
。
在 C# 中,你不能这么确定。这就是 C# 中的“函数”:
public T Id<T>(T x);
现在,由于子类型化,您可以这样称呼它:
var pers = Id<Person>(new Student());
虽然pers
是 type Person
,但Id
函数的参数不是。事实上pers
,它可能有一个更具体的类型,而不仅仅是Person
. Person
甚至可以是抽象类型,保证pers
将具有更具体的类型。
如您所见,即使使用像id
.NET 类型系统这样简单的功能,也已经比 .NET 中更严格的类型系统提供了更多的功能Haskell
。虽然这对于做一些编程工作可能很有用,但它也使得仅仅通过查看事物的类型来推理程序变得更加困难(这在 Haskell 中很有趣)。
第二件事是,Haskell 中通过一种称为“类型类”的机制存在临时多态性(也称为重载)。
equals :: Eq a => a -> a -> Bool
此函数检查两个值是否相等。但不仅仅是任何两个值,只是具有Eq
类实例的值。这有点像 C# 中类型参数的约束:
public bool Equals<T>(T x, T y) where T : IComparable
但是,有区别。一方面,子类型化:你可以用它实例化它并用它Person
调用它。Student
Teacher
但是这也有不同之处。C# 代码几乎完全按照它的类型编译。类型检查器确保参数实现正确的接口,并且比你好。
而 Haskell 代码符合如下内容:
equals :: EqDict -> a -> a -> Bool
该函数有一个额外的参数,一个包含所有它需要做的Eq
事情的函数的字典。以下是如何使用这个函数,以及它编译成的内容:
b1 = equals 2 4 --> b1 = equals intEqFunctions 2 4
b2 = equals True False --> b2 = equals boolEqFunctions True False
这也显示了是什么让子类型化如此痛苦,如果可能的话,想象一下。
b3 = equals someStudent someTeacher
--> b3 = equals personEqFunctions someStudent someTeacher
字典应该如何personEqFunctions
确定 aStudent
是否等于 a Teacher
?他们甚至没有相同的字段。
简而言之,虽然 Haskell 类型约束乍一看可能看起来像 .NET 类型约束,但它们的实现方式完全不同,并且编译成两个完全不同的东西。