Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
882 views
in Technique[技术] by (71.8m points)

haskell - What would be the "distinct method" that Traversable has in addition to Foldable?

Foldable is a superclass of Traversable, similarly to how Functor is a superclass of Applicative and Monad.

Similar to the case of Monad, where it is possible to basically implement fmap as

liftM :: Monad m => (a->b) -> m a -> m b
liftM f q = return . f =<< q

we could also emulate foldMap as

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))

using the Monoid m => (,) m monad. So the combination of superclass and methods bears in both cases a certain redundancy.

In case of monads, it can be argued that a “better” definition of the type class would be (I'll skip applicative / monoidal)

class (Functor m) => Monad m where
  return :: a -> m a
  join :: m (m a) -> m a

at least that's what's used in category theory. This definition does, without using the Functor superclass, not permit liftM, so it is without this redundancy.

Is a similar transformation possible for the Traversable class?


To clarify: what I'm after is a re-definition, let's call it,

class (Functor t, Foldable t) => Traversable t where
  skim :: ???

such that we could make the actual Traverse methods top-level functions

sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)

but it would not be possible to make generically

instance (Traversable t) => Foldable t where
  foldMap = ... skim ...

data T
instance Traversable T where
  skim = ...

I'm not asking because I need this for something particular; it's a conceptual question so as to better understand the difference between Foldable and Traversable. Again much like Monad vs Functor: while >>= is much more convenient than join for everyday Haskell programming (because you usually need precisely this combination of fmap and join), the latter makes it simpler to grasp what a monad is about.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Super hand-wavy because it's late, but the extra power that Traversable has over Foldable is a way to reconstruct the original structure. For example, with lists:

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

I think this myTraverse behaves the same as traverse, using only the classes Applicative, Foldable, and Monoid. You could re-write it to use foldr instead of foldMap if you wanted to get rid of Monoid.

lists are easy because they're a flat structure. However, I strongly suspect that you could use a Zipper to get the proper reconstruction function for any structure (since zippers are generically derivable, they should always exists).

But even with a zipper, you don't have any way of indicating that structure to the monoid/function. Notionally, it seems Traversable adds something like

class Traversed t where
  type Path t :: *
  annotate :: t a -> [(Path t, a)]
  fromKeyed :: [(Path t, a)] -> t a

this seems to overlap heavily with Foldable, but I think that's inevitable when trying to associate the paths with their constituent values.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...