views:

106

answers:

4

So I'm writing a program which returns a procedure for some given arithmetic problem, so I wanted to instance a couple of functions to Show so that I can print the same expression I evaluate when I test. The trouble is that the given code matches (-) to the first line when it should fall to the second.

{-# OPTIONS_GHC -XFlexibleInstances #-}

instance Show (t -> t-> t) where  
 show (+) = "plus"
 show (-) = "minus"  

main = print [(+),(-)]

returns

[plus,plus]

Am I just committing a motal sin printing functions in the first place or is there some way I can get it to match properly?

edit:I realise I am getting the following warning:

Warning: Pattern match(es) are overlapped
         In the definition of `show': show - = ...

I still don't know why it overlaps, or how to stop it.

+4  A: 

It overlaps because it treats (+) simply as a variable, meaning on the RHS the identifier + will be bound to the function you called show on.

There is no way to pattern match on functions the way you want.

sepp2k
+9  A: 

Here's a way to think about this. Consider:

answer = 42
magic = 3

specialName :: Int -> String
specialName answer = "the answer to the ultimate question"
specialName magic = "the magic number"
specialName x = "just plain ol' " ++ show x

Can you see why this won't work? answer in the pattern match is a variable, distinct from answer at the outer scope. So instead, you'd have to write this like:

answer = 42
magic = 3

specialName :: Int -> String
specialName x | x == answer = "the answer to the ultimate question"
specialName x | x == magic = "the magic number"
specialName x = "just plain ol' " ++ show x

In fact, this is just what is going on when you write constants in a pattern. That is:

digitName :: Bool -> String
digitName 0 = "zero"
digitName 1 = "one"
digitName _ = "math is hard"

gets converted by the compiler to something equivalent to:

digitName :: Bool -> String
digitName x | x == 0 = "zero"
digitName x | x == 1 = "one"
digitName _ = "math is hard"

Since you want to match against the function bound to (+) rather than just bind anything to the symbol (+), you'd need to write your code as:

instance Show (t -> t-> t) where  
 show f | f == (+) = "plus"
 show f | f == (-) = "minus"

But, this would require that functions were comparable for equality. And that is an undecidable problem in general.

You might counter that you are just asking the run-time system to compare function pointers, but at the language level, the Haskell programmer doesn't have access to pointers. In other words, you can't manipulate references to values in Haskell(*), only values themselves. This is the purity of Haskell, and gains referential transparency.

(*) MVars and other such objects in the IO monad are another matter, but their existence doesn't invalidate the point.

MtnViewMark
It all becomes clear! I tried making (+) and (-) instances of Eq as well but that obviously (now) doesn't make sense either.Thanks for the great answer.
Sean D
+8  A: 

As sepp2k and MtnViewMark said, you can't pattern match on the value of identifiers, only on constructors and, in some cases, implicit equality checks. So, your instance is binding any argument to the identifier, in the process shadowing the external definition of (+). Unfortunately, this means that what you're trying to do won't and can't ever work.

A typical solution to what you want to accomplish is to define an "arithmetic expression" algebraic data type, with an appropriate show instance. Note that you can make your expression type itself an instance of Num, with numeric literals wrapped in a "Literal" constructor, and operations like (+) returning their arguments combined with a constructor for the operation. Here's a quick, incomplete example:

data Expression a = Literal a
                  | Sum (Expression a) (Expression a)
                  | Product (Expression a) (Expression a)
                  deriving (Eq, Ord, Show)

instance (Num a) => Num (Expression a) where
    x + y = Sum x y
    x * y = Product x y
    fromInteger x = Literal (fromInteger x)

evaluate (Literal x) = x
evaluate (Sum x y) = evaluate x + evaluate y
evaluate (Product x y) = evaluate x * evaluate y

integer :: Integer
integer = (1 + 2) * 3 + 4

expr :: Expression Integer
expr = (1 + 2) * 3 + 4

Trying it out in GHCi:

> integer
13
> evaluate expr
13
> expr
Sum (Product (Sum (Literal 1) (Literal 2)) (Literal 3)) (Literal 4)
camccann
That's fantastic, thanks. (And far more elegant than the solution I gave below.)
Sean D
@Sean D: It also makes a nice illustration of where your mega hack would fail--consider what would happen if it was used with an operator on `Expression Integer` values. `Sum 6 2` does not equal `Literal 8`, despite my code being a perfectly reasonable instance of `Num`.
camccann
A: 

Solved it myself with a mega hack.

instance (Num t) => Show (t -> t-> t) where  
show op =
    case (op 6 2) of
        8 -> "plus"
        4 -> "minus"
        12 -> "times"
        3 -> "divided"
Sean D