为什么非多态类型不能在 Haskell 中实现 Foldable?

Why can't a non-polymorphic type implement Foldable in Haskell?

假设我有一些简化的 lisp 风格 Expr 类型如下

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

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

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

据此我想为 Expr 实现 Foldable 以折叠这些缺点列表 - 但这是不可能的,因为 Foldable 需要一种类型 * -> *(即只有一个类型参数的多态),其中我的 Expr 有种类 *.

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

To me it seems like folding over non-polymorphic types like in this case would be perfectly reasonable, but obviously I'm missing something.

确实很有道理。折叠单形容器的一种方法是使用 MonoFoldable. Another is using a Fold from lens,或来自其他光学库:

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 个实例的种类是 * -> * 而不是 *,我将其归结为简单性和历史原因的结合。从历史上看,FoldableTraversable 的一个分支,值得注意的是,虽然单态遍历很有用,但它们的局限性比影响单态折叠的那些更显着(例如,您可以'从他们那里恢复 fmap,但只是一个单态的 omap)。最后,Joseph Sible Is there anything we lose with MonoFoldable? 建议的问答包括一些有趣的讨论,其中讨论了不将 Foldable 完全替换为 MonoFoldable 的潜在原因。