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

时间:2019-04-16 19:15:43

标签: haskell polymorphism functor fold foldable

说我有一些简化的Lisp样式Expr类型,如下所示

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

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

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

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

那是为什么?在我看来,像这样折叠非多态类型是完全合理的,但显然我缺少了一些东西。

1 个答案:

答案 0 :(得分:5)

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

It is perfectly reasonable indeed. One way of folding a monomorphic container is using MonoFoldable. Another is using a Fold from lens, or from some other optics library:

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}

As for why Foldable instances have kind * -> * rather than *, I would put it down to a combination of simplicity and historical reasons. Historically speaking, Foldable is an offshoot of Traversable, and it is worth noting that, while monomorphic traversals can be useful, their limitations are rather more striking than those which affect monomorphic folds (for instance, you can't recover fmap from them, but merely a monomorphic omap). Finally, the Q&A suggested by Joseph Sible, Is there anything we lose with MonoFoldable?, includes some interesting discussion of potential reasons for not outright replacing Foldable with MonoFoldable.