【发布时间】:2020-10-13 20:09:09
【问题描述】:
我正在尝试正确地自学 Purescript。我目前正在阅读Purescript book 并在第 6 章。由于我对 Haskell 非常熟悉,所以到目前为止练习非常简单。但我目前坚持一个,我当然知道“如何”做,但似乎被关于定义类型类实例的 Purescript 特定行为所击败。这让我非常好奇这是如何在实践中完成的。
我正在尝试做的具体事情是为NonEmpty 类型定义一个Foldable 实例,其定义如下:
data NonEmpty a = NonEmpty a (Array a)
来自 Haskell 背景,我知道 foldMap 往往是定义 Foldable 实例的最简单方法,所以我没有时间写:
instance foldableNonEmpty :: Foldable NonEmpty where
foldMap f (NonEmpty a as) = f a <> foldMap f as
这在 Haskell 中就足够了,因为所有其他 Foldable 方法都有默认值 foldMap。
我想在 PureScript 中也足够了,但是我的代码无法编译,给我错误:
The following type class members have not been implemented:
foldr :: forall a b. (a -> b -> b) -> b -> ... -> b
foldl :: forall a b. (b -> a -> b) -> b -> ... -> b
in type class instance
Data.Foldable.Foldable NonEmpty
这让我感到惊讶,充其量似乎不方便。但我检查了documentation,很快发现确实有预定义的方法可以从foldMap 获取foldl 和foldr,形式为foldlDefault 和foldrDefault。所以我的下一个尝试是:
instance foldableNonEmpty :: Foldable NonEmpty where
foldMap f (NonEmpty a as) = f a <> foldMap f as
foldr = foldrDefault
foldl = foldlDefault
但这也无法编译。这次的错误是:
The value of foldableNonEmpty is undefined here, so this reference is not allowed.
这对我来说有点神秘,但我认为这意味着我还不能访问 foldrDefault 和 foldlDefault,因为(根据 Pursuit 文档)它们要求类型构造函数已经有一个 @987654341 @instance,因此不能用作定义实例的一部分。
但这一切当然引出了一个问题:如何在 Purescript 中定义 Foldable 实例,而不必手动为 3 种方法中的 2 种写出冗余定义?与其他根据彼此定义方法的类型类类似。或者如果有可能(我希望如此!),我错过了什么?
其实,在谷歌搜索了一下之后,我确实找到了一个Foldable 实例的示例,发现如果我这样做,该实例确实可以编译:
instance foldableNonEmpty :: Foldable NonEmpty where
foldMap f (NonEmpty a as) = f a <> foldMap f as
foldr f = foldrDefault f
foldl f = foldlDefault f
但这引出了一个新问题:据我所知,由于 PureScript 使用与 Haskell 完全相同的方式使用柯里化,为什么允许这样做而上面的“eta-reduced”版本却不允许?关于未定义值的错误消息与什么有什么关系?
【问题讨论】:
标签: typeclass purescript