6

我是 Haskell 的新手,对类型类的工作方式有点困惑。这是我正在尝试做的事情的简化示例:

data ListOfInts = ListOfInts {value :: [Int]}
data ListOfDoubles = ListOfDoubles {value :: [Double]}

class Incrementable a where
    increment :: a -> a

instance Incrementable ListOfInts where
    increment ints = map (\x -> x + 1) ints

instance Incrementable ListOfDoubles where
    increment doubles = map (\x -> x + 1) doubles

(我意识到增加列表的每个元素可以非常简单地完成,但这只是一个更复杂问题的简化版本。)

编译器告诉我我有多个value. 如果我更改和的定义ListOfInts如下ListOfDoubles

type ListOfInts = [Int]
type ListOfDoubles = [Double]

然后编译器说“'Incrementable ListOfInts' 的非法实例声明”(对于 'Incrementable ListOfInts' 也是如此ListOfDoubles。如果我使用 newtype,例如 ,newtype ListOfInts = ListOfInts [Int]那么编译器会告诉我“无法将预期类型 'ListOfInts' 与实际类型 '[b0]' 匹配” "(对于ListOfDoubles.

我对类型类的理解是它们促进了多态性,但我显然遗漏了一些东西。在上面的第一个示例中,编译器是否只看到 type 参数引用了a一个带有一个名为一个字段的类型是s 的列表,另一个字段的类型是 s 的列表)?对于其他尝试也是如此?valueincrementIntDouble

提前致谢。

4

1 回答 1

17

你真的看到了两个不同的问题,所以我会这样解决它们。

第一个是与value领域。Haskell 记录以一种稍微特殊的方式工作:当您命名一个字段时,它会作为一个函数自动添加到当前范围。本质上,你可以想到

data ListOfInts = ListOfInts {value :: [Int]}

作为语法糖:

data ListOfInts = ListOfInts [Int]

value :: ListOfInt -> [Int]
value (ListOfInts v) = v

因此,拥有两个具有相同字段名称的记录就像拥有两个具有相同名称的不同函数一样——它们重叠。这就是为什么你的第一个错误告诉你你已经声明values了多次。

解决这个问题的方法是在使用记录语法的情况下定义你的类型,就像我在上面所做的那样:

data ListOfInts = ListOfInts [Int]
data ListOfDoubles = ListOfDoubles [Double]

当您使用type代替时data,您只是创建了一个类型同义词而不是一个新类型。使用

type ListOfInts = [Int]

表示ListOfInts与 just 相同[Int]。由于各种原因,默认情况下您不能在类实例中使用类型同义词。这是有道理的——很容易犯错误,比如尝试编写一个 for 的实例[Int]以及一个 for的实例ListOfInts,这会破坏。

data用于包装单个类型,如[Int]or[Double]与 using相同newtype。但是,newtype它的优点是它根本不携带运行时开销。因此,编写这些类型的最佳方法确实是newtype

newtype ListOfInts = ListOfInts [Int]
newtype ListOfDoubles = ListOfDoubles [Double]

需要注意的重要一点是,当您使用dataor时newtype,如果您想获取其内容,还必须“解包”该类型。您可以通过模式匹配来做到这一点:

instance Incrementable ListOfInts where
  increment (ListOfInts ls) = ListOfInts (map (\ x -> x + 1) ls)

这会解开ListOfInts,在其内容上映射一个函数并将其包装起来。

只要您以这种方式解开值,您的实例就应该可以工作。

在旁注中,您可以使用称为“运算符部分”的内容编写map (\ x -> x + 1)为。map (+ 1)这意味着你隐式地创建了一个 lambda 来填充操作符缺少的任何参数。大多数人发现该map (+ 1)版本更易于阅读,因为不必要的噪音更少。

于 2013-05-03T09:23:52.313 回答