So we have the free monad: (encoding may vary, but they're all the same)
data Free f a = Pure a
| Impure (f (Free f a))
instance Functor f => Monad (Free f) where
pure = Pure
Pure x >>= f = f x
Impure x >>= f = Impure ((>>= f) <$> x)
liftFree :: Functor f => f a -> Free f a
liftFree x = Impure (Pure <$> x)
runFree :: Monad g => (forall x. f x -> g x) -> Free f a -> g a
runFree _ (Pure x) = pure x
runFree f (Impure x) = join (runFree f <$> f x)
such that runFree
forms a monad homomorphism, which is the defining property of the free monad.
runFree f (pure x) = pure x
runFree f (liftFree x >>= liftFree . g) = f x >>= (f . g)
-- + some other associativity requirements
We can also make a similar construction to (what I believe to be) the free Bind
from semigroupoids, which is a functor with only bind >>-
:
data Free1 f a = Done (f a)
| More (f (Free1 f a))
instance Functor f => Bind (Free f) where
Done x >>- f = More (f <$> x)
More x >>- f = More ((>>- f) <$> x)
liftFree1 :: f a -> Free1 f a
liftFree1 = Done
runFree1 :: Bind g => (forall x. f x -> g x) -> Free1 f a -> g a
runFree1 f (Done x) = f x
runFree1 f (More x) = f x >>- runFree1 f
and we get the appropriate bind homomorphism:
such that runFree1
forms a bind homomorphism, which is the defining property:
runFree1 f (liftFree1 x >>- liftFree1 . g) = f x >>- (f . g)
-- + some associativity laws
Now, these two types are great. We can convert from Free1
to Free
, which makes sense:
toFree :: Free1 f a -> Free f a
toFree (Done x) = Impure (Pure <$> x)
toFree (More x) = Impure (toFree <$> x)
but converting backwards is trickier. To go from a Free
to a Free1
, we would have to handle two cases:
- The
Free
is pure, so cannot be represented inFree1
. - The
Free
is impure, so can be represented inFree1
.
It makes sense that these two cases can be handled statically, since we can just match on Pure
or Impure
.
So a reasonable type signature might be:
fromFree :: Functor f => Free f a -> Either a (Free1 f a)
but I am having problems writing this.
fromFree :: Free f a -> Either a (Free1 f a)
fromFree (Pure x) = Left x -- easy
fromFree (Impure x) = Right ?? -- a bit harder
It looks like the main problem is that we need to decide whether or not to use the Done
or the More
constructor without ever "running" the f. We need a:
f (Free f a) -> Free1 f a
which sounds like it might be troublesome for functors were you can't "get out", like IO
.
So, this sounds impossible, unless I'm missing something.
There's another encoding that I've tried:
data Free1 f a = Free1 (f (Free f a))
this does lets us define fromFree
, and it borrows from the NonEmpty
construction (data NonEmpty a = a :| [a]
). And I was able to use this approach when defining the "free Apply
", which was nice. This does let us write toFree
, fromFree
, liftFree1
, and a Bind
instances. However, I can't seem to write runFree1
:
runFree1 :: Bind g => (forall x. f x -> g x) -> f (Free f a) -> g a
as soon as I do anything, I seem to require return :: a -> g a
, but we don't have this for all Bind g
(I found a possible version that typechecks, but it performs the effects twice and so is not a proper bind homomorphism)
So, while this method gives us fromFree
, I can't seem to find a way to write runFree1
, which is the very thing that gives it "free Bind
" capabilities.
Of these two methods, either we:
- Have an actual free
Bind
withrunFree1
, but it is imcompatible withFree
in that you can't "match" aFree
into either aFree1
, or a pure value. - Have a type that is compatible with
Free
(maybe a "nonemptyFree
"), but is not actually a freeBind
(norunFree1
), and defeats the whole purpose.
From this I can make one of two conclusions:
- There is some way to make a free Bind
Free1
that is compatible withFree
, but I have not been able to figure it out yet - Fundamentally, we cannot have a free
Bind
that is compatible withFree
. This seems to contradict my intuition (we can always immediately decide if aFree
is pure or impure, so we should also be able to immediately distinguish between pure (zero effects) orFree1
), but maybe there is a deeper reason that I am missing out on?
Which of these is the case? If #1, what is way, and if #2, what is the deeper reason? :)
Thank you in advance!
Edit To dispel my uncertainty about whether or not I was working with a "true Free Bind", I started looking at one that was truly a Free Bind by definition:
newtype Free1 f a = Free1 { runFree1 :: forall g. Bind g => (forall x. f x -> g x) -> g a }
And I can't seem to be able to write fromFree
for this one, either. In the end I seem to need a g (Either a (Free1 a)) -> g a
.
If I can't write fromFree
for this, then it stands to reason that I can't write fromFree
for any implementation of the free bind, since all implementations are isomorphic to this one.
Is there a way to write fromFree
for this one, even? Or is it all impossible :'( It all worked so well for Alt
/Plus
and Apply
/Applicative
.