What does Traversable is to Applicative contexts mean? - haskell

I am trying to understand Traversable with the help of https://namc.in/2018-02-05-foldables-traversals.
Somewhere the author mentioned the following sentence:
Traversable is to Applicative contexts what Foldable is to Monoid
values.
What did he try to clarify?
I do not get the connection between Foldable to Monoid.
Please provide an example.

In the beginning, there was foldr:
foldr :: (a -> b -> b) -> b -> [a] -> b
and there was mapM:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
foldr was generalized to data types other than [a] by letting each type define its own definition of foldr to describe how to reduce it to a single value.
-- old foldr :: (a -> b -> b) -> b -> [] a -> b
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
If you have a monoid, you don't have to specify a binary function, because the Monoid instance already provides its own starting value and knows how to combine two values, which is apparent from its default definition in terms of foldr:
-- If m is a monoid, it provides its own function of type b -> b.
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
foldMap f = foldr (mappend . f) mempty
Traverse does the same kind of generalization from lists to traversable types, but for mapM:
-- old mapM :: Monad m => (a -> m b) -> [] a -> m ([] b)
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
(When mapM was first defined, there was no Applicative class; if there had been, mapA :: Applicative f => (a -> f b) -> [a] -> f [b] could have been defined instead; the Monad constraint was stronger than was necessary.)
An Applicative is monoidal in nature, so there was no need in Traverse for the type of distinction that foldr/foldMap draws.

The article (and the corresponding passage of the Wikibook) is talking about how the effects (or, to use the language seen there, the contexts) of applicative values can be combined monoidally. The connection with Foldable is that list-like folding is ultimately amounts to combining values monoidally (see chepner's answer, and also Foldr/Foldl for free when Tree is implementing Foldable foldmap?. As for the applicative contexts part, there are a few ways to look at that. One of them is noting that, for any Applicative f and Monoid m, f m is a monoid, with pure mempty as mempty and liftA2 mappend as mappend (this Ap type from the reducers package witnesses that). For a concrete example, let's pick f ~ Maybe and m ~ (). That leaves us with four possible combinations:
liftA2 mappend (Just ()) (Just ()) = Just ()
liftA2 mappend (Just ()) Nothing = Nothing
liftA2 mappend Nothing (Just ()) = Nothing
liftA2 mappend Nothing Nothing = Nothing
Now contrast with All, the Bool monoid with (&&) as mappend:
mappend (All True) (All True) = All True
mappend (All True) (All False) = All False
mappend (All False) (All True) = All False
mappend (All False) (All False) = All False
They match perfectly: Just () stands for True, Nothing for False, and liftA2 mappend for (&&).
Now let's have another look at the Wikibook example:
deleteIfNegative :: (Num a, Ord a) => a -> Maybe a
deleteIfNegative x = if x < 0 then Nothing else Just x
rejectWithNegatives :: (Num a, Ord a, Traversable t) => t a -> Maybe (t a)
rejectWithNegatives = traverse deleteIfNegative
GHCi> rejectWithNegatives [2,4,8]
Just [2,4,8]
GHCi> rejectWithNegatives [2,-4,8]
Nothing
The Maybe values generated by applying deleteIfNegative to the values in the lists are combined monoidally in the way shown above, so that we get a Nothing unless all the Maybe values are Just.
This matter can also be approached in the opposite direction. Through the Applicative instance for Const...
-- I have suppressed a few implementation details from the instance used by GHC.
instance Monoid m => Applicative (Const m) where
pure _ = Const mempty
Const x <*> Const y = Const (x `mappend` y)
... we can get an Applicative out of any Monoid, such that (<*>) combines the monoidal values monoidally. That makes it possible to define foldMap and friends in terms of traverse.
On a final note, the category theoretical description of Applicative as a class for monoidal functors involves something rather different from what I have covered here. For further discussion of this issue, and of other fine print, see Monoidal Functor is Applicative but where is the Monoid typeclass in the definition of Applicative? (if you want to dig deeper, it is well worth it to read all of the answers there).

Related

Can I write `foldr` (or `foldMap`) in terms of 'recursion schemes' `cata`?

I recently read about recursion schemes where catamorphisms are described as analogous to generalized foldr.
Is is possible to write an instance of Foldable (via either foldr or foldMap) in terms of cata in all cases?
foldMap, being the fundamental operation of Foldable, is a better candidate for implementation than foldr. The answer is a qualified yes. cata only handles recursion; it doesn't tell you where to "find" all the values in a structure. (In the same way, implementing foldMap #[] with foldr still requires knowing the inner details of [].) Doing so requires a little help:
class Bifoldable f where
bifoldMap :: Monoid m => (a -> m) -> (b -> m) -> f a b -> m
You can then define
foldMapDefault ::
(Recursive (f a), Base (f a) ~ b a, Bifoldable b) =>
Monoid m => (a -> m) -> f a -> m
foldMapDefault f = cata (bifoldMap f id)
This allows you to do things like
data Tree a = Leaf | Branch (Tree a) a (Tree a)
makeBaseFunctor ''Tree
deriveBifoldable ''TreeF
instance Foldable Tree where foldMap = foldMapDefault
(Though you may as well have just said deriving Foldable on Tree.) For maximum genericity, you may want something more like this (I say "want"...)
newtype Fixed f a = Fixed { getFixed :: f a }
newtype Bibase f a b = Bibase { getBibase :: Base (f a) b }
instance (forall a. Recursive (f a), Bifoldable (Bibase f)) =>
Foldable (Fixed f) where
foldMap :: forall a m. Monoid m => (a -> m) -> Fixed f a -> m
foldMap f = cata (bifoldMap f id . Bibase #f #a #m) . getFixed
You can now say things like
data Tree a = Leaf | Branch (Tree a) a (Tree a)
makeBaseFunctor ''Tree
deriveBifoldable ''TreeF
deriving via TreeF instance Bifoldable (Bibase Tree)
deriving via (Fixed Tree) instance Foldable Tree
But now your Base functors can be more irregular:
data List a = Nil | Cons a (List a)
type instance Base (List a) = Compose Maybe ((,) a)
instance Recursive (List a) where
project Nil = Compose Nothing
project (Cons x xs) = Compose (Just (x, xs))
instance Bifoldable (Bibase List) where
bifoldMap f g (Bibase (Compose Nothing)) = mempty
bifoldMap f g (Bibase (Compose (Just (x, xs)))) = f x <> g xs
deriving via (Fixed List) instance Foldable List
You often can, but not universally. All it takes is a single counter-example. Several exist, but consider the simplest one that comes to (my) mind.
While completely unnecessary, you can define Boolean values with an F-algebra:
data BoolF a = TrueF | FalseF deriving (Show, Eq, Read)
instance Functor BoolF where  
fmap _  TrueF =  TrueF
  fmap _ FalseF = FalseF
From this (as the linked article explains) you can derive the catamorphism:
boolF :: a -> a -> Fix BoolF -> a
boolF x y = cata alg
  where alg TrueF = x
 alg FalseF = y
The type Fix BoolF is isomorphic to Bool, which isn't parametrically polymorphic (i.e. it doesn't have a type parameter), yet a catamorphism exists.
The Foldable type class, on the other hand, is defined for a parametrically polymorphic container t, e.g.
foldr :: (a -> b -> b) -> b -> t a -> b
Since Bool isn't parametrically polymorphic, it can't be Foldable, yet a catamorphism exists. The same is true for Peano numbers.
For parametrically polymorphic types, on the other hand, you often (perhaps always?) can. Here's a Foldable instance for a tree defined with its catamorphism:
instance Foldable TreeFix where
  foldMap f = treeF (\x xs -> f x <> fold xs)
Here's one for Maybe:
instance Foldable MaybeFix where
foldMap = maybeF mempty
and one for linked lists:
instance Foldable ListFix where
foldr = listF

Restricting type parameter to Monoid

I've previously defined a function which takes a list of Maybes and turns it into a Maybe of a list, like so:
floop :: [Maybe a] -> Maybe [a]
floop [] = Just []
floop (Nothing:_) = Nothing
floop (Just x:xs) = fmap (x:) $ floop xs
Now I want to redefine it to be compatible with a larger class of containers, not just lists, and I've found that it needs to implement the functions foldr, mappend, mempty, fmap , and pure; so I figure that the following type line would be appropriate:
floop :: (Foldable t, Functor t, Monoid t) => t (Maybe a) -> Maybe (t a)
As (I think) it ensures that those functions are implemented for the given container, however it leads to the following error:
Expecting one more argument to ‘t’
The first argument of ‘Monoid’ should have kind ‘*’,
but ‘t’ has kind ‘* -> *’
In the type signature for ‘floop'’:
floop' :: (Foldable t, Functor t, Monoid t) =>
t (Maybe a) -> Maybe (t a)
After looking into it, I found Monoid's kind is different to that of Functor and Foldable, but I can't see why that would be the case, nor how to correct the error.
For those interested, here's the current implementation:
floop :: (Foldable t, Functor t, Monoid t) => t (Maybe a) -> Maybe (t a)
floop xs = let
f :: (Foldable t, Functor t, Monoid t) => Maybe a -> Maybe (t a) -> Maybe (t a)
f Nothing _ = Nothing
f (Just x) ys = fmap (mappend $ pure x) ys
in
foldr f (Just mempty) xs
Note: I have been made aware that this already exists as a builtin function (sequence), but I intend to implement it as a learning exercise.
Monoidal applicatives are described by the Alternative class, using (<|>) and empty instead of mappend and mempty:
floop :: (Foldable t, Alternative t) => t (Maybe a) -> Maybe (t a)
floop xs = let
f :: (Foldable t, Alternative t) => Maybe a -> Maybe (t a) -> Maybe (t a)
f Nothing _ = Nothing
f (Just x) ys = fmap ((<|>) $ pure x) ys
in
foldr f (Just empty) xs
This might be a good place to bring up hoogle.
Searching for t (m a)-> m (t a) returns sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a) as the first result. This then leads to the Traversable type class which is fairly close to what you are looking for.
As Lee said you could use the Alternative class which is the Applicative equivalent of Monoid. Slightly more generalized:
sequence' :: (Alternative t, Foldable t, Applicative a) => t (a b) -> a (t b)
sequence' = foldr step (pure empty)
where step = liftA2 prepend
prepend = (<|>) . pure
Here prepend first wraps some single element into t so it can use (<|>) to prepend it. liftA2 lets us abstract over the applicative a, you can imagine it as unwrapping two arguments, applying them to prepend and wrapping the result back up.

Fold on non-lists?

In a recent assignment I have been asked to define fold functions for some non-list types. I'm still not quite able to wrap my head around this concept yet. Thus far, I have understood fold as performing over subsequent elements in the list. fold on Tree still do make intuitive sense, since one is able to apply some function recursively over the root's sub-trees.
However, on a datatype like:
Maybe a :: Nothing | Just a
There is no list (as it seems to me) to perform the fold action on.
I'm sure I have some issue with understanding the basic concepts here, and I would greatly appreciate some clearing up.
Foldable is a pretty confusing class, to be honest, because it doesn't have an awful lot of laws and it's quite possible to write quite a lot of different Foldable instances for almost any given type. Fortunately, it's possible to figure out what a Foldable instance should do in a purely mechanical way based on a Traversable instance for the same type—if there is one.
We have
class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
Traversable has several different laws, but it turns out the most important one is traverse Identity = Identity. Let's see how this applies to Maybe:
traverse :: Applicative f => (a -> f b) -> Maybe a -> f (Maybe b)
traverse g Nothing = _none
traverse g (Just a) = _some
Now in the first case, you need to produce f (Maybe b), and all you have is g :: a -> f b. Since you don't have any f values, and you don't have any a values, the only thing you can produce is pure Nothing.
In the second case, you have to produce f (Maybe b) and you have g :: a -> f b and a. So the only interesting way to start is to apply g to a, getting g a :: f b. Now you have two options to consider: you could throw away this value, and just return Nothing, or you can wrap it up in Just.
By the identity law, traverse Identity (Just a) = Identity (Just a). So you're not allowed to return Nothing. The only legal definition is
traverse _ Nothing = pure Nothing
traverse g (Just a) = Just <$> g a
The Traversable instance for Maybe is completely determined by the Traversable laws and parametricity.
Now it's possible to fold using traverse:
foldMapDefault :: (Traversable t, Monoid m)
=> (a -> m) -> t a -> m
foldMapDefault f xs =
getConst (traverse (Const . f) xs)
As this applies to Maybe,
foldMapDefault f Nothing =
getConst (traverse (Const . f) Nothing)
foldMapDefault f (Just a) =
getConst (traverse (Const . f) (Just a))
Expanding our definitions,
foldMapDefault f Nothing = getConst (pure Nothing)
foldMapDefault f (Just a) = getConst (Just <$> (Const (f a)))
By the definitions of pure and <$> for Const, these are
foldMapDefault f Nothing = getConst (Const mempty)
foldMapDefault f (Just a) = getConst (Const (f a))
Unwrapping the constructor,
foldMapDefault f Nothing = mempty
foldMapDefault f (Just a) = f a
And this is indeed exactly how foldMap is defined for Maybe.
As "basic concepts" go, this is pretty mind-bending, so don't feel too bad.
It may help to set aside your intuition about what a fold does to a list and think about what type a specific folding function (let's use foldr) should have if applied to a Maybe. Writing List a in place of [a] to make it clearer, the standard foldr on a list has type:
foldr :: (a -> b -> b) -> b -> List a -> b
Obviously, the corresponding fold on a Maybe must have type:
foldrMaybe :: (a -> b -> b) -> b -> Maybe a -> b
Think about what definition this could possibly have, given that it must be defined for all a and b without knowing anything else about the types. As a further hint, see if there's a function already defined in Data.Maybe that has a similar type -- maybe (ha ha) that'll give you some ideas.

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.
Foldable is to Functor as Traversable is to Monad, i.e. Foldable and Functor are superclasses of Monad and Traversable (modulo all the applicative/monad proposal noise).
Indeed, that's already in the code
instance Foldable f => Traversable f where
...
So, it's not clear what more there is to want. Foldable is characterized by toList :: Foldable f => f a -> [a] while Traversable depends ultimately on not only being able to abstract the content as a list like toList does, but also to be able to extract the shape
shape :: Functor f => f a -> f ()
shape = fmap (const ())
and then recombine them
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
which depends on traverse.
For more information on this property see this blog post by Russell O'Connor.
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.

Can liftM differ from liftA?

According to the Typeclassopedia (among other sources), Applicative logically belongs between Monad and Pointed (and thus Functor) in the type class hierarchy, so we would ideally have something like this if the Haskell prelude were written today:
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Pointed f where
pure :: a -> f a
class Pointed f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
-- either the traditional bind operation
(>>=) :: (m a) -> (a -> m b) -> m b
-- or the join operation, which together with fmap is enough
join :: m (m a) -> m a
-- or both with mutual default definitions
f >>= x = join ((fmap f) x)
join x = x >>= id
-- with return replaced by the inherited pure
-- ignoring fail for the purposes of discussion
(Where those default definitions were re-typed by me from the explanation at Wikipedia, errors being my own, but if there are errors it is at least in principle possible.)
As the libraries are currently defined, we have:
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b
and:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap :: (Monad m) => m (a -> b) -> m a -> m b
Note the similarity between these types within each pair.
My question is: are liftM (as distinct from liftA) and ap (as distinct from <*>), simply a result of the historical reality that Monad wasn't designed with Pointed and Applicative in mind? Or are they in some other behavioral way (potentially, for some legal Monad definitions) distinct from the versions that only require an Applicative context?
If they are distinct, could you provide a simple set of definitions (obeying the laws required of Monad, Applicative, Pointed, and Functor definitions described in the Typeclassopedia and elsewhere but not enforced by the type system) for which liftA and liftM behave differently?
Alternatively, if they are not distinct, could you prove their equivalence using those same laws as premises?
liftA, liftM, fmap, and . should all be the same function, and they must be if they satisfy the functor law:
fmap id = id
However, this is not checked by Haskell.
Now for Applicative. It's possible for ap and <*> to be distinct for some functors simply because there could be more than one implementation that satisfies the types and the laws. For example, List has more than one possible Applicative instance. You could declare an applicative as follows:
instance Applicative [] where
(f:fs) <*> (x:xs) = f x : fs <*> xs
_ <*> _ = []
pure = repeat
The ap function would still be defined as liftM2 id, which is the Applicative instance that comes for free with every Monad. But here you have an example of a type constructor having more than one Applicative instance, both of which satisfy the laws. But if your monads and your applicative functors disagree, it's considered good form to have different types for them. For example, the Applicative instance above does not agree with the monad for [], so you should really say newtype ZipList a = ZipList [a] and then make the new instance for ZipList instead of [].
They can differ, but they shouldn't.
They can differ because they can have different implementations: one is defined in an instance Applicative while the other is defined in an instance Monad. But if they indeed differ, then I'd say the programmer who wrote those instances wrote misleading code.
You are right: the functions exist as they do for historical reasons. People have strong ideas about how things should have been.

Resources