对我来说,在这种情况下折叠非多态类型似乎是完全合理的,但显然我错过了一些东西。
这确实是完全合理的。折叠单态容器的一种方法是使用MonoFoldable
. 另一个是使用from Fold
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
实例有 kind* -> *
而不是*
,我将其归结为简单性和历史原因的结合。从历史上看,Foldable
是 的一个分支Traversable
,值得注意的是,虽然单态遍历可能有用,但它们的局限性比影响单态折叠的那些限制更显着(例如,你不能fmap
从它们中恢复,而只是单态omap
)。最后,Joseph Sible 提出的问答,MonoFoldable 我们有什么损失吗?Foldable
, 包括一些关于不完全替换为 .的潜在原因的有趣讨论MonoFoldable
。