traverse
and sequenceA
both need to deal with what happens when the Traversable
is empty. Then you won't have any elements in an Applicative
context that you can use to glom other stuff onto so you'll need pure
.
The definitions you've presented are a bit misleading since, as you pointed out, they're mutually dependent. When you go to actually implement one of them you'll run into the empty collection problem. And you'll run into the need for <*>
as Functor
provides no facility to aggregate different values of f a
for some functor f
.
Therefore the Applicative
constraint is there because for most types, in order to implement either traverse
or sequenceA
you'll need the tools that Applicative
provides.
That being said there are certain types where you don't need pure
or don't need <*>
. If your collection can never be empty you don't need pure
, e.g. NonEmpty
. If your collection never has more than one element you don't need <*>
, e.g. Maybe
. Sometimes you don't need either and you can get away with just fmap
, e.g. a tuple section such as (a,)
).
Haskell could have a more fine-grained typeclass hierarchy that breaks Applicative
down into more fine-grained parts with separate classes for pure
and <*>
which would then allow you to make different versions of Traversable
with weaker constraints. Edward Kmett's library semigroupoids
goes in this direction, although it isn't perfect since it can't add actual superclasses to the base
classes. It has Apply
which is Applicative
but without pure
, and Traversable1
which is a variant of Traversable
that uses Apply
instead of Applicative
and thus requires that its types can never be empty.
Note that other ecosystems have chosen to have a more fine-grained typeclass hierarchy (see Scala's cats
or scalaz
libraries). I personally find such a distinction occasionally useful but not overwhelmingly so.
As for your second question if all you know how to do is tear down something, you can still perform effects along the way but you can't necessarily recover the original structure. Hence why sequenceA_
is in Foldable
. It is strictly less powerful than sequenceA
.