4

假设我有一些简化的 lisp 样式Expr类型如下

data Expr = String String | Cons Expr Expr
  deriving (Show)

我可以将列表创建为 Cons-cells 的 Cons-cells:

Cons (String "hello") (Cons (String "World") (String "!"))

从这里我想实现FoldableforExpr来折叠这些 cons 列表 - 但这是不可能的,因为Foldable需要一种类型* -> *(即,只有一个类型参数的多态),而我的Expr有 kind *

这是为什么?对我来说,在这种情况下折叠非多态类型似乎是完全合理的,但显然我错过了一些东西。

4

1 回答 1

5

对我来说,在这种情况下折叠非多态类型似乎是完全合理的,但显然我错过了一些东西。

这确实是完全合理的。折叠单态容器的一种方法是使用MonoFoldable. 另一个是使用from Foldlens其他一些光学库:

import Control.Lens

data Expr = String String | Cons Expr Expr
  deriving (Show)

-- A Traversal can also be used as a Fold.
-- strings :: Applicative f => (String -> f String) -> (Expr -> f Expr) 
strings :: Traversal' Expr String
strings f (String s) = String <$> f s
strings f (Cons l r) = Cons <$> strings f l <*> strings f r 
GHCi> hello = Cons (String "hello") (Cons (String "World") (String "!"))
GHCi> toListOf strings hello
["hello","World","!"]
GHCi> import Data.Monoid
GHCi> foldMapOf strings (Sum . length) hello
Sum {getSum = 11}

至于为什么Foldable实例有 kind* -> *而不是*,我将其归结为简单性和历史原因的结合。从历史上看,Foldable是 的一个分支Traversable,值得注意的是,虽然单态遍历可能有用,但它们的局限性比影响单态折叠的那些限制更显着(例如,你不能fmap从它们中恢复,而只是单态omap)。最后,Joseph Sible 提出的问答,MonoFoldable 我们有什么损失吗?Foldable, 包括一些关于不完全替换为 .的潜在原因的有趣讨论MonoFoldable

于 2019-04-17T01:00:52.853 回答