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
Freeis pure, so cannot be represented inFree1. - The
Freeis 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
BindwithrunFree1, but it is imcompatible withFreein that you can't "match" aFreeinto 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
Free1that is compatible withFree, but I have not been able to figure it out yet - Fundamentally, we cannot have a free
Bindthat is compatible withFree. This seems to contradict my intuition (we can always immediately decide if aFreeis 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.
Free (Integer ->)andFree1 (Integer ->). - n. 1.8e9-where's-my-share m.Free1, or of all "freeBind" implementations in general? (which would imply that myFree1is not a proper free Bind) - Justin L.type FreeBind f a = forall g. Bind g => (forall x. f x -> g x) -> g a) and satisfies all the free bind laws. In this version I cannot implementfromFreeeither, so it might be the case that this is true for all Free Binds. - Justin L.BindandMonad, but not forApplyandApplicative, orAltandPlus. Both of these can be properly converted, but notBindandMonad. - Justin L.