Foldable
是 Traversable
的超类,类似于 Functor
是 Applicative
和 Monad
的超类。
类似于 Monad
的情况,可以基本上将 fmap
实现为:
liftM :: Monad m => (a->b) -> m a -> m b
liftM f q = return . f =<< q
我们还可以将foldMap
模拟为
foldLiftT :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldLiftT f = fst . traverse (f >>> \x -> (x,x))
-- or: . sequenceA . fmap (f >>> \x -> (x, x))
使用
Monoid m => (,) m
单子。因此,超类和方法的组合在两种情况下都有一定的冗余。在单子的情况下,可以认为类型类的“更好”定义是(我将跳过applicative / monoidal)。
class (Functor m) => Monad m where
return :: a -> m a
join :: m (m a) -> m a
至少在范畴论中是这样使用的。这个定义不使用Functor
超类,不允许liftM
,因此它没有这种冗余。
Traversable
类是否可以进行类似的转换?
澄清一下:我想重新定义它,我们称之为
class (Functor t, Foldable t) => Traversable t where
skim :: ???
这样我们就可以将实际的 Traverse
方法变成顶级函数。
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
但是通常无法通用化
instance (Traversable t) => Foldable t where
foldMap = ... skim ...
data T
instance Traversable T where
skim = ...
我提出这个概念性问题并不是因为我特别需要它; 而是为了更好地理解 Foldable
和 Traversable
之间的区别。就像 Monad
和 Functor
一样:在日常 Haskell 编程中,>>=
比 join
更方便(因为你通常需要精确地结合 fmap
和 join
),但后者使得理解单子变得更加简单。
traverse
实现Foldable
。 - leftaroundaboutFoldable
是Traversable
的超类。超类应该可以根据子类来实现。 - Gabriella GonzalezTraversableMinusFoldable
,使得class(Foldable t,TraversableMinusFoldable t)=> Traversable t where
没有新函数,但现有函数是兼容的。也就是说,你能否在不引用Foldable
中的任何内容的情况下定义traverse
。但即使是这个问题,我也不确定这是否是研究这种情况的正确方式。旧的Monad很混乱,最好忘记它 :-) - misterbeeTraversable
方法提供“最弱签名”,以便实际使用需要Foldable
超类。目前,你可以像在历史悠久的Monad
中那样省略超类,但正如你所说的那样并不好。 - leftaroundabout