In these kinds of situations, typed holes can help with how to proceed. They give information about the type the still unimplemented "hole" should have.
Using a typed hole instead of error
in your definition:
instance Functor f => MyMonad (Free f) where
ret = Var
flatMap (Var x) g = f x
flatMap (Node xs) g = _
Gives an error message like (here simplified):
Found hole `_' with type: Free f b
...
Relevant bindings include
g :: a -> Free f b (bound at Main.hs:10:21)
xs :: f (Free f a) (bound at Main.hs:10:17)
flatMap :: Free f a -> (a -> Free f b) -> Free f b
(bound at Main.hs:9:3)
That Free f b
in the hole... which constructor should it have? Var
or Node
?
Now, a value of type Free a
is like a tree that has values of type a
on the leaves (the Var
constructor) and whose branching nodes are "shaped" by the functor f
.
What is >>=
for Free
? Think of it as taking a tree and "grafting" new trees on each of its leaves. These new trees are constructed from the values in the leaves using the function that is passed to >>=
.
This helps us continue: now we know that the constructor on the right of the flatMap (Node xs) f = _
pattern must be Node
, because "grafting" new things onto the tree never collapses already existing nodes into leaves, it only expands leaves into whole new trees.
Still using type holes:
instance Functor f => MyMonad (Free f) where
ret = Var
flatMap (Var x) g = f x
flatMap (Node xs) g = Node _
Found hole `_' with type: f (Free f b)
...
Relevant bindings include
g :: a -> Free f b (bound at Main.hs:10:21)
xs :: f (Free f a) (bound at Main.hs:10:17)
In xs
we have a Free f a
wrapped in a f
, but f
is a functor and we could easily map over it.
But how to convert that Free f a
into the Free f b
required by the hole? Intuitively, this Free f a
will be "smaller" that the one the >>=
started with, because we have stripped one "branching node". Maybe it is even a leaf node, like the case covered by the other pattern-match! This suggests using recursion of some kind.