在阅读了 Haskell 中的类型类之后,我认为它们就像 Java 接口 您在类型类 (Num) 和任何
data/newtype
(Foo) 声明的类型中定义一组方法,如果它想成为 part of
类型类,则必须实现该类型类的所有方法。
但这有什么用呢? 这与多态性有何关系? 是参数化多边形还是临时多边形?
这就是 ghci 中定义
Num
类型类的方式
type Num :: * -> Constraint
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
{-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
-- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
instance Num Float -- Defined in ‘GHC.Float’
...
首先,第一个
type Num :: * -> Constraint
是什么意思(?)
之后,它说,如果一个类型a
想要成为约束/部分Num,它应该实现以下功能。
下面显示了该类型类的一些实例,如Double
、Float
这看起来像java接口。
所以,为了让
Foo
成为这个Num
的成员我们需要做类似的事情
instance Num Foo where
(+) foo1 foo2 = foo1
...
但是它有什么用(?) 我明白它们是如何定义的
首先,kind签名
* -> Constraint
意味着Num
是采用类型(*
)并返回Constraint
的东西。
A
Constraint
是一个接口,即类型必须以实现某些方法的形式满足的契约。
多态性与此相关,因为您可以使用约束来编写对类型通用的函数,但有一些更细粒度的要求。例如:假设您有一个函数想要在列表中查找元素。类型可能是
find :: a -> [a] -> Int
其中返回的类型是位置,类型变量
a
是对象和列表的类型。但是等等...在该方法的任何实现中,在某些时候,我必须使用 x :: a
将 as :: [a]
与 =
中的元素进行比较。所以它在 a
上并不是真正通用的,我们需要强加一个 Constraint
来表示“a
必须实现与 =
运算符相等的概念”。那么类型签名就变成了
find :: Eq a => a -> [a] -> Int
=>
不像函数箭头,它更像是在说“只要满足约束Eq a
,那么......”