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
monad。因此,超类和方法的组合在两种情况下都有一定的冗余。
如果是monads,可以认为类型类的“更好”定义是(我将跳过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
vs Functor
非常相似:>>=
对于日常Haskell编程比join
更方便(因为您通常需要这个组合 fmap
和join
),后者使得更容易掌握monad是什么。
答案 0 :(得分:3)
Foldable
是Functor
,Traversable
是Monad
,即Foldable
和Functor
是Monad
的超类Traversable
(模拟所有申请/ monad提案噪音)。
确实,这已经在代码中了
instance Foldable f => Traversable f where
...
所以,目前还不清楚还有什么需要。 Foldable
的特点是toList :: Foldable f => f a -> [a]
,而Traversable
最终不仅取决于能够像toList
这样的列表抽象内容,还能够提取形状
shape :: Functor f => f a -> f ()
shape = fmap (const ())
然后将它们重新组合
combine :: Traversable f => f () -> [a] -> Maybe (f a)
combine f_ = evalStateT (traverse pop f_) where
pop :: StateT [a] Maybe a
pop = do x <- get
case x of
[] = empty
(a:as) = set as >> return a
取决于traverse
。
有关此媒体资源的详情,请参阅this blog post by Russell O'Connor。
答案 1 :(得分:3)
超级手工波浪,因为它已经很晚了,但Traversable
超过Foldable
的额外力量是重建原始结构的一种方式。例如,使用列表:
module MyTraverse where
import Data.Foldable
import Data.Traversable
import Control.Applicative
import Data.Monoid
data ListRec f x = ListRec
{ el :: f (Endo [x])
}
instance Applicative f => Monoid (ListRec f x) where
mempty = ListRec (pure mempty)
mappend (ListRec l) (ListRec r) =
ListRec (mappend <$> l <*> r)
toM :: Functor f => f b -> ListRec f b
toM this = ListRec $ (Endo . (:)) <$> this
fromM :: Functor f => ListRec f b -> f [b]
fromM (ListRec l) = flip appEndo [] <$> l
myTraverse :: Applicative f => (a-> f b) -> [a] -> f [b]
myTraverse f xs = fromM $ foldMap (toM . f) xs
我认为此myTraverse
与traverse
的行为相同,仅使用类Applicative
,Foldable
和Monoid
。如果您想摆脱foldr
,可以重新编写它以使用foldMap
代替Monoid
。
列表很容易,因为它们是扁平结构。但是,我强烈怀疑你可以使用Zipper为任何结构获得正确的重建功能(因为拉链通常是可导出的,它们应该始终存在)。
但即使使用拉链,也没有任何方法可以指示monoid / function的结构。从理论上讲,似乎Traversable
增加了类似
class Traversed t where
type Path t :: *
annotate :: t a -> [(Path t, a)]
fromKeyed :: [(Path t, a)] -> t a
这似乎与Foldable
重叠,但我认为在尝试将路径与其组成值相关联时,这是不可避免的。