tags:

views:

249

answers:

5

Code:

data Exp a = Const a | Eq (Exp a) (Exp a)

I want the Const a to contain a value of type show so that i can print it later. So in C# i would write:

class Const : Exp { IShow X; }
class Eq : Exp { Exp X, Y; }

How can i do that in Haskell?

+4  A: 

You can do this by saying

data (Show a) => Exp a = Const a | Eq (Exp a) (Exp a)

But this is almost always a bad idea because it forces every function that uses Exp to mention the show constraint, even if it never uses the Show methods. Instead, put the show constraint on just the functions it's relevant for. See Real World Haskell for an explanation.

Nathan Sanders
What if i want *Eq (Const 0) (Const "")*? I could not have different types in the same expression this way. How to solve that?
usr
If you're writing an interpreter with different semantics from Haskell (where `0==""` is a type error) the Right Thing is to create a type for values in *your* language instead of directly reusing Haskell's: `data IObj = IInt Int | IStr String |...`. Now change `Exp` to `data Exp = Const IObj | Eq IObj IObj`. Otherwise you'll find it difficult to write an `equals` function that can inspect two different types.
Nathan Sanders
Nathan, i think this is the right way to go. I want to write an ORM so i need to be able to check equality of a column with Int or String.
usr
+4  A: 
{-# LANGUAGE GADTs #-}

data Exp a where
    Const :: Show a => a -> Exp a
    Eq :: Exp a -> Exp a -> Exp a

If you want to allow for varying data types in different branches of Eq that's fine too.

data Exp where
    Const :: Show a => a -> Exp
    Eq :: Exp -> Exp -> Exp
ephemient
What is the purpose of putting this kind of constraint on a data constructor as you have done in your first example? Any function needing to call `show` on an Exp will still have to include that constraint in its signature.The only reason I can think of doing this is to provide a hint on what the "a" type variable should actually be. Is there any other benefit I might be missing?
Michael Steele
`Eq (Const 0) (Const "")` will be legal, for one. And no, a `Show` constraint will not be necessary, as the type system has guaranteed that a `Const` cannot be packed with anything lacking a `show`. See http://www.haskell.org/haskellwiki/GADT
ephemient
In your *second* example I now see that it must be this way. Functions taking Exp arguments are unable to specify class constraints since the Exp type itself is not polymorphic.
Michael Steele
I think this is the best answer as it is basically a Haskell version of my C# example. I did not know that the different constructors can have different type parameters.
usr
A: 

To answer the second question, asked in comments, Eq (Const 0) (Const "") is not achievable with the datatype you have because Exp Integer and Exp String are not the same type. One option is to do something like

data Exp = forall a . Show a => Const a | Eq Exp Exp

Whether that will do you any good depends on what you're planning on doing with the type.

Edit: This does require language extensions to be enabled as mentioned.

Dan
Might want to note that this needs `{-# LANGUAGE ExistentialQuantification #-}` or `-XExistentialQuantification` or similar. And personally I like the GADT style better, but it's equivalent.
ephemient
Very true, I forgot that. I guess for whatever reason I've found existential quantification easier to wrap my head around than GADT but on closer examination it looks like they are in fact equivalent in this case.
Dan
+2  A: 

If all you want to know about the argument to Const is that you can show it, why not just store the resulting String value in the constructor instead? For example:

data Exp = Const String | Eq Exp Expr

example = Eq (Const (show 0)) (Const (show ""))

This closely resembles your C# version.

Martijn
And for bonus points, add a smart constructor: mkConst :: Show a => a -> Exp mkConst = Const . showSo that the example becomes: `example = Eq (mkConst 0) (mkConst "")` Normally I would use the lowercase constructor name as function name, but that one is already taken...
yatima2975
That is quite clever. Lazy evaluation to the rescue.
usr
A: 

I would just declare your datatype an instance of the type class Show:

data Exp a = Const a | Eq (Exp a) (Exp a)

instance (Show a) => Show (Exp a) where
    show (Const a) = show a
    show (Eq x y ) = "[ " ++ show x ++ " , " ++ show y ++ " ]"

Look what happens when you load this in ghci and do:

*Main> let x = Eq (Const 1) (Eq (Const 2) (Const 3))
*Main> x      
[1 , [2 , 3] ]

Answering comment:

You can easily deal with different types. Suppose you want to parse mathematical expressions. You can have the following structure, for example:

data Expr  = Var String | Sum (Expr) (Expr) | Number Int | Prod (Expr) (Expr)

This is enough to represent any expression made of sums and products of numbers and named variables. For example:

x = Sum (Var "x") (Prod (Number 5) (Var "y"))

represents: x + 5y

To print this beautifully I'd do:

instance Show Expr where
    show (Var s) = show s
    show (Sum x y) = (show x) ++ " + " (show y)
    show (Prod x y) = (Show x) ++ (show y)
    show (Number x) = show x

This would do the trick. You could also use GADTs:

 data Expr where
      Var :: String -> Expr
      Sum :: Expr -> Expr -> Expr

etc... and then instantiate this as Show.

Rafael S. Calsaverini
This does not work as i want the branches of eq to be able to contain different types.
usr