I've just been trying to wrap my head around free monads; as a learning aid, I've managed to write a Show
instance for the following Free
type:
{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}
-- Free monad datatype
data Free f a = Return a | Roll (f (Free f a))
instance Functor f => Monad (Free f) where
return = Return
Return a >>= f = f a
Roll ffa >>= f = Roll $ fmap (>>= f) ffa
-- Show instance for Free; requires FlexibleContexts and
-- UndecidableInstances
instance (Show (f (Free f a)), Show a) => Show (Free f a) where
show (Return x) = "Return (" ++ show x ++ ")"
show (Roll ffx) = "Roll (" ++ show ffx ++ ")"
-- Identity functor with Show instance
newtype Identity a = Id a deriving (Eq, Ord)
instance Show a => Show (Identity a) where
show (Id x) = "Id (" ++ show x ++ ")"
instance Functor (Identity) where
fmap f (Id x)= Id (f x)
-- Example computation in the Free monad
example1 :: Free Identity String
example1 = do x <- return "Hello"
y <- return "World"
return (x ++ " " ++ y)
The use of UndecidableInstances
bothers me somewhat; is there a way to do without it? All that Google yields is this blog post by Edward Kmett, which comfortingly has basically the same Show
class definition as I do.
You actually can eliminate the UndecidableInstance requirement for
Show
here, though you can't do the same thing forRead
orEq
.The trick is to replace the contents of your functor with something you can show more directly, but that you don't tell anyone else about. Consequently, we'll limit our exports to just:
and bang out a data type for things we can only
show
.Now, if we never tell anyone about
Showable
, the only instances forShow (f Showable)
will be instances that were polymorphic in the argument toa
, constrained at most up to a Show instance. This is sound reasoning as long as the end user isn't actively trying to subvert your code using other extensions. Some hinkiness is possible with the addition of functional dependencies and/or overlapping/undecidable instances but only things that subvert intent, nothing that can cause you to crash.With that out of the way we can build a decidable
Show
instance.The implementation given here doesn't eliminate the need for
FlexibleContexts
, but you can eliminate that too -- if you really feel the need for Haskell 98 compatibility -- by writing a couple of additional class layers.I use this trick in a couple of packages -- including my
ad
package -- to reduce the need for undecidable instances.