I have some questions concerning the function hoistfree from the Haskell library Control.Monad.Free. Given a transformation f
between two functors, hoistfree f produces a morphism between the corresponding free monads. Here is its definition.
hoistFree :: Functor g => (forall a. f a -> g a) -> Free f b -> Free g b
hoistFree _ (Pure a) = Pure a
hoistFree f (Free as) = Free (hoistFree f <$> f as)
Question 1 How does Haskell know that <$>
is the map associated to g
and not to f
, Free f
or Free g
?
Question 2 Why hoistfree has not been defined as
hoistFree :: Functor g => (forall a. f a -> g a) -> Free f b -> Free g b
hoistFree _ (Pure a) = Pure a
hoistFree f (Free as) = Free (f (hoistFree f <$> as))
?
If f
is a natural transformation, these two definitions coincide. The second definition however always satisfies the relation
hoistfree f = iter (wrap . f) . map return
which looks pretty natural. Furthermore, there are a few basic functions that can be expressed using iter_map f g = iter f . map g
. For example,
(=<<) f = iter_map wrap f
Question 3 Is iter_map defined somewhere? It looks like a monadic mapreduce. I didn't see it in the base library. Is there some gain in fusioning iter and map? In a few other languages, this is the case, but I am not sure for Haskell.
Question 1
Because of type inference, which chooses <$>
from g
. Indeed, in
Free (hoistFree f <$> f as)
f as
has type g <something>
, hence the <$>
is the one given by Functor g
.
Question 2
I think that, in Haskell, f
is always a natural transformation. Any polymorphic function f a -> g a
must be natural in a
, by parametricity / free theorem.
Both definitions being equivalent, I'm not sure if any one is the "best". Maybe yours is. Or maybe the original one has better performance in practice. It looks a bit as the foldr
vs foldl'
argument on associative operators, where there's no clear winner.
Question 3 No idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With