gpt4 book ai didi

haskell - Haskell 类型类中应该如何使用类型?

转载 作者:行者123 更新时间:2023-12-04 13:16:52 25 4
gpt4 key购买 nike

我是 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' 的非法实例声明”(对于 ListOfDoubles 也是如此。如果我使用新类型,例如 newtype ListOfInts = ListOfInts [Int],那么编译器会告诉我“无法将预期类型 'ListOfInts' 与实际匹配键入 '[b0]'"(对于 ListOfDoubles 也是如此。

我对类型类的理解是它们促进了多态性,但我显然遗漏了一些东西。在上面的第一个示例中,编译器是否只看到类型参数 a指的是具有名为 value 的字段的记录并且看来我正在尝试定义 increment以多种方式查看此类型(而不是查看两种不同的类型,一种具有类型为 Int 的列表的字段,另一种具有类型为 Double 的列表的字段)?对于其他尝试也是如此?

提前致谢。

最佳答案

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

第一个是 value field 。 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[Int] 相同.由于各种原因,默认情况下您不能在类实例中使用类型同义词。这是有道理的——很容易犯错误,比如尝试为 [Int] 编写实例。以及一个用于 ListOfInts ,这将打破。

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

需要注意的重要一点是,当您使用 datanewtype ,如果您想获取其内容,还必须“解包”该类型。您可以通过模式匹配来做到这一点:
instance Incrementable ListOfInts where
increment (ListOfInts ls) = ListOfInts (map (\ x -> x + 1) ls)

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

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

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

关于haskell - Haskell 类型类中应该如何使用类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16355282/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com