我对Haskell和函数式编程都比较新,对它的语法感到有些不舒服。
在以下代码中,=>
是什么意思?还有(Num a,Ord a)
?
loop :: (Num a, Ord a) => a -> (t -> t) -> t -> t
(Num a,Ord a) => ...
表示 loop
可以适用于任何类型 a
,只要该类型是 Num
和 Ord
类型类的实例,分别对应数字类型和排序类型。基本上,您可以将 loop
视为 =>
右侧的类型,除了需要 a
是 Num
和 Ord
的实例之外。
您可以将类型类视为与 OOP 接口基本相似(但它们不是同一件事!)-它们封装了一组定义,任何实例都必须支持,并且可以使用这些定义编写通用代码。例如,Num
包括数字运算,如加法和乘法,而 Ord
包括小于、大于等运算。
有关类型类的更多信息,请参见 《Learn You a Haskell》 中的 此介绍。
=>
符号分隔类型签名的两个部分:
因此,你可以将(Num a, Ord a) => a -> (t -> t) -> t -> t
理解为“该类型是a -> (t -> t) -> t -> t
,同时a
必须有一个Num
实例并且a
还必须有一个Ord
实例”。
有关类型类的更多信息,请参见http://www.learnyouahaskell.com/types-and-typeclasses
一种思考方式是,Ord a
和 Num a
是函数的附加输入。不过它们是一种特殊的输入:字典。当你使用该函数与特定类型 a
时,必须为类型 a
上的 Ord
和 Num
操作也提供字典。
任何使用具有字典输入函数的函数都必须具有相同的字典输入。
foo :: (Num a, Ord a) => a -> t
foo x = loop x someFunc someT
然而,您不必明确传递这些字典。 哈斯凯尔将为您处理,只要有可用的字典。 您可以使用类型类实例创建字典。
instance Num MyType with
x + y = ...
x - y = ...
...
这将为MyType
上的Num
操作创建一个字典,因此MyType
可以在任何需要输入Num a
的地方使用(当然,前提是它满足其他要求)。
=>
的左边,您声明用于右侧使用的类型的限制条件。a
被限制为同时是Ord
类型类和Num
类型类的实例。