了解 Haskell class 与 OO 语言中的类非常不同是很重要的。它是一个类型类,即它“将类型的集合组合在一起”,而 OO 类将值的集合组合在一起。 IOW,OO 类是一种类型(包含值),但 Haskell 类更像是一种类型类型。
现在,函数/方法总是将值作为参数并返回值作为结果。但它不能给你一个类型,也不能给你一个“类型类的值”,因为没有这样的东西。
相反,使用类型类的方法是这样的:您编写一个多态函数,即接受或产生一些未指定类型的值的函数。这就是 类型变量 的用途。通常,多态签名意味着函数可以处理任何类型的值,比如
length :: [a] -> Int
它接受一个列表并且不关心其元素的类型。但是你说你确实关心一下使用了什么类型,也就是说,你要求它们在类中。这是一个约束,它是用=> 表示法编写的。 toInt其实有签名(类声明自动生成)
toInt :: Id a => a -> Int
坦率地说,我怀疑这真的是你想要的。如果您对Id a 所能做的就是将其转换为Int,那么结果实际上是一个Int(只是带有一个类型注释,它指示这个Id 是什么类型的对象属于),不管a参数。因此,您应该考虑使用 Id 类型,而不是 Id 类
newtype Id' a = Id {toInt :: Int} -- The prime ' symbol has no particular meaning,
-- I just use it for disambiguation.
那么,你有更明智的类型
toInt :: Id' a -> Int
不需要任何约束。
newtype 更类似于 OO 类,因为它实际上定义了一个具有可以传递的值的具体类型。
getId 方法也是一个受约束的多态函数,它可以获取“具有Id”的内容并返回该 ID。在这种情况下,一个类是有意义的(因为你可以有不同的数据结构,可能以不同的方式存储它们的 ID)。这实际上是
class HasId a where
getId :: a -> Id' a
根据Welperooni's answer,确实也可以使用您原来的Id 类定义HasId,只是它并没有多大意义。 Id 是可以用作标识符的所有类型的类。您可以表示 getId 可以产生任何此类类型的 Id:
class HasId a where
getId :: Id b => a -> b
请注意,result-Id 类型现在完全独立于您要计算其 Id 的对象的类型。 b 始终可以是 any Id 类型。正如我所说,这没有意义:为什么您需要一大堆不同的类型来表示 Id,但需要所有 HasId 类型来支持所有这些?
事实上,这无法按现状实现,因为没有机制允许您生成任意Id 类型的Id。这将需要一个额外的方法
class Id a where
toInt :: a -> Int
fromInt :: Int -> a
现在你可以做
class HasId a where
getId :: Id b => a -> b
data SomeObj { objName :: String
, objId :: Int }
instance HasId SomeObj where
getId (SomeObj _ i) = fromInt i
但是,如果您要求任何Id 类型可以从Int 转换为Int,那么这些类型必须与Int 本身同构。因此,newtype Id' a 方法几乎肯定更好。