Note that this proof isn't actually necessary, as the result in question is indeed a free theorem. See Reid Barton's answer.
I believe this will do:
traverse f . fmap g -- LHS
By the fmap/traverse
law,
traverse f . runIdentity . traverse (Identity . g)
Since fmap
for Identity
is effectively id
,
runIdentity . fmap (traverse f) . traverse (Identity . g)
The Compose
law offers a way to collapse two traversals into one, but we must first introduce Compose
using getCompose . Compose = id
.
runIdentity . getCompose . Compose . fmap (traverse f) . traverse (Identity . g)
-- Composition law:
runIdentity . getCompose . traverse (Compose . fmap f . Identity . g)
Again using the Identity
fmap
,
runIdentity . getCompose . traverse (Compose . Identity . f . g)
Compose . Identity
is an applicative transformation, so by naturality,
runIdentity . getCompose . Compose . Identity . traverse (f . g)
Collapsing inverses,
traverse (f . g) -- RHS
Invoked laws and corollaries, for the sake of completeness:
-- Composition:
traverse (Compose . fmap g . f) = Compose . fmap (traverse g) . traverse f
-- Naturality:
t . traverse f = traverse (t . f) -- for every applicative transformation t
-- `fmap` as traversal:
fmap g = runIdentity . traverse (Identity . g)
The latter fact follows from the identity law, traverse Identity = Identity
, plus the uniqueness of fmap
.