haskellmonads

Monads for Functional Programming, 2.2 Variation one: Exceptions


I've been working on understanding Monads in Haskell a bit better so I've started reading Philip Wadler's paper Monads for functional programming. In order to internalize the principles a bit better, and expose myself to as much Haskell code as possible, I decided I would code up and test all the examples as I go through the paper.

Right off 2.2 Variation one: Exceptions is giving me a little trouble. Here is my code.

data Term = Con Int | Div Term Term
data M a = Raise Exception | Return a
type Exception = String

answer, error_ :: Term
answer = (Div (Div (Con 1972) (Con 2)) (Con 23))
error_ = (Div (Con 1) (Con 0))

eval :: Term -> M Int
eval (Con a) = Return a
eval (Div t u) = case eval t of 
                    Raise e -> Raise e
                    Return a ->
                        case eval u of 
                            Raise e -> Raise e
                            Return b ->
                                if  b == 0 
                                    then Raise "divide by zero"
                                    else Return (a `div` b)

I can load the code into GHCi, but when I try to run

eval answer

it throws the error

No instance for (Show (M Int)) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it

I read this post which explains why ‘print’ is being called and concluded that maybe I needed to add an instance to Show for M a. But when I added

instance Show (M a) where
show (M a) = show a

to my code I tried to reload the file I got this error.

Not in scope: data constructor ‘M’

This confused me at first but this post explains that I was defining a type constructor not a data constructor.

Anyway, I feel like I am going down a rabbit hole that may or may not have the solution and I figured I'd post the question over here. My code looks to be line-for-line the same as his code. What changes do I need to make to make the code run in GHCi?


Solution

  • The added Show instance will not work for two reasons:

    1. there is no constraint that says that the a of M a is an instance of Show; and
    2. the M data type has no data constructor M, it has a Raise, and a Return.

    So a straightforward implementation would look like:

    instance Show a => Show (M a) where
        show (Raise a) = "Raise " ++ show a
        show (Return a) = "Return " ++ show a
    

    But we can save us the trouble, and let Haskell automatically derive an instance for Show with:

    data M a = Raise Exception | Return a deriving Show