Quantcast
Channel: How to write an expression over a data type and print it out in haskell? - Stack Overflow
Viewing all articles
Browse latest Browse all 2

Answer by Zeta for How to write an expression over a data type and print it out in haskell?

$
0
0

First of all, expr has the wrong type. expr :: Poly would be correct. You could also ask GHCi about that type:

> :t Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var VarAdd (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var :: Poly

In order to use print or a similar function, the type needs to be an instance of Show, since

print :: Show a => a -> IO ()

One way to achieve this is to simply derive the Show instance automatically:

data Poly = .... deriving (Show)

However, this won't lead to your desired result "x^3 + x + 1". You need to write the Show instance yourself, for example:

instance Show Poly where    show (Add x y) = "("++ show x ++") * ("++ show y ++")    show (Mul x y) = "("++ show x ++") * ("++ show y ++")    show (Lit x)   = show x    show (Var)     = "x"

Note that this still isn't what you're looking for:

> show expr"(1) + ((x) + ((x) * ((x) * (x))))"

However, that information should enable you to create your own instance.


Another way to solve this problem is to implement the showsPrec method of the Show typeclass. This function threads through a precedence so that you can choose when to apply parentheses, allowing you to print out an expression that looks more like 1 + x + x * x * x instead of the simple example using show above. The type of showsPrec is a little wonky, though, it looks like

showsPrec :: Show a => Int -> a -> ShowS

Where

type ShowS = String -> String

The ShowS type is just encoding a diff list, which allows more efficient construction of Strings through function composition rather than simple concatenation. For this Poly type, you would want to implement it something like this:

instance Show Poly where    showsPrec d poly = case poly of        Lit i   -> shows i        Var     -> showString "x"        Add l r -> showParen (d > add_prec)                 $ showsPrec add_prec l                 . showString "+"                 . showsPrec add_prec r        Mul l r -> showParen (d > mul_prec)                 $ showsPrec mul_prec l                 . showString " * "                 . showsPrec mul_prec r        where            -- infixl 6 +            add_prec = 6            -- infixl 7 *            mul_prec = 7

The d parameter represents the precedence. Each call we check if the precedence is greater than that of each operator, and if so it adds parentheses around that expression (showParen conditionally puts parentheses around a ShowS), and then it builds the left and right trees of the expression with the correct precedence. The precedences of 6 and 7 come from asking GHCi :i (+) and :i (*), which show the fixity (precedence) of each operator respectively.

With another instance we can use this to write very readable instances as well:

instance Num Poly where    (+) = Add    (*) = Mul    negate = Mul (Lit (-1))    fromInteger = Lit    abs = undefined    signum = undefined

Keep in mind that this isn't entirely well behaved due to the undefineds, but it allows us to write code like

x :: Polyx = Varexpr1 :: Polyexpr1 = 1 + x + x * x * xexpr2 :: Polyexpr2 = 2 * (x + 3 * x)expr3 :: Polyexpr3 = (4 + x) * (4 * (x + 2) * x + x * (x + x + 4))

And to test:

> :l poly.hs> expr11 + x + x * x * x> expr22 * (x + 3 * x)> expr3(4 * x) * (4 * (x + 2) * x + x * (x + x + 4))

Which is identical to how we defined it in source code.


Viewing all articles
Browse latest Browse all 2

Trending Articles