开发者

Haskell question: constraining data types to use show

开发者 https://www.devze.com 2022-12-13 05:03 出处:网络
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:

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?


{-# 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


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.


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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消