Minimize parenthesis when printing expression - haskell

I have a simple arithmetic expression data structure that I want to be able to print. For sake of simplicity here I have made an example with 3 binary operations, addition, multiplication and division. The definition looks like this:
module ExprPrint where
import Text.Printf
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
instance Show Expr where
show (Lit x) = show x
show (Add e1 e2) = printf "(%s) + (%s)" (show e1) (show e2)
show (Mul e1 e2) = printf "(%s) * (%s)" (show e1) (show e2)
show (Div e1 e2) = printf "(%s) / (%s)" (show e1) (show e2)
The goal I have is to print the data structure while removing all superfluous parenthesis. of course the naive show function I have implemented above includes way too many of them. So what I want to do is make the Show instance take the precedence (Div and Mul over Add) and associativity(Add and Mul are associative while Div is left-associative) of the operations into account.
Here are some examples:
one = Lit 1
-- Shows "((1) + (1)) + (1)" but should be 1 + 1 + 1
addAssoc = show $ Add (Add one one) one
-- Shows "((1) * (1)) * (1)" but should be 1 * 1 * 1
mulAssoc = show $ Mul (Mul one one) one
-- Shows "((1) / (1)) / (1)" but should be 1 / 1 / 1
divAssoc = show $ Div (Div one one) one
-- Shows "(1) / ((1) / (1)) but should be 1 / (1 / 1)
divAssoc2 = show $ Div one (Div one one)
-- Show "((1) * (1)) + (1)" but should 1 * 1 + 1
addPrec = show $ Add (Mul one one) one
-- Show "(1) + ((1) * (1))" but should show 1 + (1 * 1)
addPrec2 = show $ Add one (Mul one one)
Is there an "easy" to take that into account in the show instance? I think I could do it by taking all the cases into account but that would be an explosion of functions. Is there some algorithm or known way to handle this?
I hope somebody has some pointers!
Thanks.

An instance in terms of show isn't powerful enough to avoid redundant parentheses, since it doesn't have any information about precedence available. You'll need to write your instance in terms of showsPrec instead, which does, like this:
module ExprPrint where
import Text.Show
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
instance Show Expr where
showsPrec prec (Lit x) = showsPrec prec x
showsPrec prec (Add e1 e2) = showParen (prec >= 7) $ showsPrec 7 e1 . showString " + " . showsPrec 7 e2
showsPrec prec (Mul e1 e2) = showParen (prec >= 8) $ showsPrec 8 e1 . showString " * " . showsPrec 8 e2
showsPrec prec (Div e1 e2) = showParen (prec >= 8) $ showsPrec 8 e1 . showString " / " . showsPrec 8 e2
I chose 6 and 7 for your precedence levels since that's what Haskell uses for its own +, *, and div operators, but it should be obvious how you'd choose different ones.
As for associativity, there's no perfect way to do that in general, but you can fake it with some precedence tweaks in your case, since math doesn't have any operators at the same precedence levels with different associativies. Here's an example of how to do that (I added Exp, with precendence level 8, to give an example of the right-associative way to do it too):
module ExprPrint where
import Text.Show
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
| Exp Expr Expr
instance Show Expr where
showsPrec prec (Lit x) = showsPrec prec x
showsPrec prec (Add e1 e2) = showParen (prec >= 7) $ showsPrec 6 e1 . showString " + " . showsPrec 7 e2
showsPrec prec (Mul e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " * " . showsPrec 8 e2
showsPrec prec (Div e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " / " . showsPrec 8 e2
showsPrec prec (Exp e1 e2) = showParen (prec >= 9) $ showsPrec 9 e1 . showString " ^ " . showsPrec 8 e2
That's still not perfect, since it still doesn't know the associative property of Add or Mul, so Mul one (Mul one one) will show as 1 * (1 * 1) instead of 1 * 1 * 1, but I don't think there's any possible way to fix that, since division doesn't share that property, but since it has the same precedence as multiplication, you can't distinguish them in showsPrec.
Actually, you can cheat a bit more than that, by peeking at the next level down and re-associating:
module ExprPrint where
import Text.Show
data Expr = Lit Int
| Add Expr Expr
| Mul Expr Expr
| Div Expr Expr
| Exp Expr Expr
instance Show Expr where
showsPrec prec (Lit x) = showsPrec prec x
showsPrec prec (Add e1 (Add e2 e3)) = showsPrec prec (Add (Add e1 e2) e3)
showsPrec prec (Add e1 e2) = showParen (prec >= 7) $ showsPrec 6 e1 . showString " + " . showsPrec 7 e2
showsPrec prec (Mul e1 (Mul e2 e3)) = showsPrec prec (Mul (Mul e1 e2) e3)
showsPrec prec (Mul e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " * " . showsPrec 8 e2
showsPrec prec (Div e1 e2) = showParen (prec >= 8) $ showsPrec 7 e1 . showString " / " . showsPrec 8 e2
showsPrec prec (Exp e1 e2) = showParen (prec >= 9) $ showsPrec 9 e1 . showString " ^ " . showsPrec 8 e2
I think this is perfect. All of your test cases pass now.

Related

Evaluating nested boolean data type

The If data constructor (If BoolType Expr Expr) should evaluate the Boolean expression (first argument) and return the value of the second argument if it is true or return the third argument. I can construct an evaluation of the expression Expr but I don't understand how to incorporate the nested expression BoolType in order to evaluate an expression. A bit too twisty, I can't get my head around it.
Here's two data types:
data Expr = Val Int
| Add Expr Expr
| Sub Expr Expr
| Mul Expr Expr
| Div Expr Expr
| If BoolType Expr Expr
data BoolType = Lit Bool
| Or BoolType BoolType
| EqualTo Expr Expr
| LessThan Expr Expr
I wrote an expression that evaluates the type:
eval :: BoolType -> Bool
eval (Lit n) = n
eval (Or e1 e2) = eval e1 || eval e2
eval (EqualTo e1 e2) = num e1 == num e2
eval (LessThan e1 e2) = num e1 < num e2
num :: Expr -> Int
num (Val n) = n
num (Add e1 e2) = num e1 + num e2
num (Sub e1 e2) = num e1 - num e2
num (Mul e1 e2) = num e1 * num e2
num (Div e1 e2) = num e1 `div` num e2
It should evaluate out to any normal equation but how do I even incorporate an If boolean data type into the total equation?
The evaluator functions currently have incomplete pattern matches:
*Q55835635> :l 55835635.hs
[1 of 1] Compiling Q55835635 ( 55835635.hs, interpreted )
55835635.hs:22:1: warning: [-Wincomplete-patterns]
Pattern match(es) are non-exhaustive
In an equation for `num': Patterns not matched: (If _ _ _)
|
22 | num (Val n) = n
| ^^^^^^^^^^^^^^^^^^^^^^^^^...
Ok, one module loaded.
Just add the missing pattern to num:
num :: Expr -> Int
num (Val n) = n
num (Add e1 e2) = num e1 + num e2
num (Sub e1 e2) = num e1 - num e2
num (Mul e1 e2) = num e1 * num e2
num (Div e1 e2) = num e1 `div` num e2
num (If b e1 e2) = if (eval b) then num e1 else num e2
You can now evaluate expressions, including those with If expressions:
*Q55835635> num $ Add (Val 40) (If (Lit True) (Val 2) (Val 0))
42
The rest of the functions aren't necessary.
You can proceed with your approach, adding a suitable equation:
value (If cond e1 e2) = ifHelper (eval cond) (value e1) (value e2)
Then, you need to define your helper:
ifHelper :: Bool -> Maybe Int -> Maybe Int -> Maybe Int
ifHelper True m1 m2 = ...
ifHelper False m1 m2 = ...
By the way, it is usually recommended to define the type of any function before starting to write the function itself. This helps both the programmer (who can reason about what type are the arguments) and the compiler (which can produce better error messages if something goes wrong).
Turning on warnings with the -Wall flag is also a good idea, since warnings can spot several potential errors.

Taking data and turning it into a string

Given the following code:
data Exprs = Const Double
| Var String --in math we often use char
| Sqrt Exprs --we can take sqrt of anything
| IntPow Exprs Int--the easy case of exponents
| Exp Exprs --e^expr
| Ln Exprs --logarithms
| Mult Exprs Exprs--multiplication
| Add Exprs Exprs
| Neg Exprs
deriving (Show, Eq, Ord)
x = Var "x"
y = Var "y"
z = Var "z"
-- to multiply x and y we type in "Mult x y"
example = Add (Const 7) ( Mult (Const 4) (Add (Sqrt x) (Exp y)))
How would I make a function that displays 7 + 4 * (sqrt(x)) + e^y from example?
What you want to implement here is the showsPrec function in the Show typeclass. This function takes an extra argument that indicates the precedence of the operation, allowing you to more easily achieve sane parentheses, and it also is more efficient since it uses what amounts to a diff list for building the string (more efficient concatenation). The function show defaults to \x -> showsPrec 0 x "", so when you call show it will work properly. An incomplete example for your case would be
data Exprs
= Const Double
| Var String --in math we often use char
| Sqrt Exprs --we can take sqrt of anything
| IntPow Exprs Int--the easy case of exponents
| Exp Exprs --e^expr
| Ln Exprs --logarithms
| Mult Exprs Exprs--multiplication
| Add Exprs Exprs
| Neg Exprs
deriving (Eq, Ord)
instance Show Exprs where
showsPrec n (Const x) = showParen (n > 10) $ showsPrec 11 x
showsPrec n (Var var) = showParen (n > 10) $ showString var
showsPrec n (Add l r) = showParen (n > 6) $ showsPrec 7 l . showString "+" . showsPrec 7 r
showsPrec n (Mult l r) = showParen (n > 7) $ showsPrec 8 l . showString "*" . showsPrec 8 r
showsPrec n (Sqrt e) = showParen (n > 10) $ showString "sqrt(" . shows e . showString ")"
I'll leave it to you to implement the other constructors (and test it heavily to ensure there are no mistakes, I do not guarantee that this is 100% correct), but you should have a pretty good start here. You may want to experiment with :i (*), :i (+), and :i (**) to figure out where the precedences I've used have come from.
As I posted in a comment to #ThreeFx's answer, I think it's bad practice to use the Show typeclass for pretty-printing or other general string munging. The documentation for Show notes that, for derived instances, "The result of show is a syntactically correct Haskell expression" made of constructors, constants, etc. That's not a rule per se, but it's a very helpful invariant. When you evaluate an expression in ghci, for instance, you expect to get a result that you can then copy-paste and reuse in code. Using Show to perform the operation you want breaks that expectation.
Rather, I think you should expose a function--not show--that you can properly document, etc., and that doesn't violate the implicit contract on Show instances. #Bakuriu suggested as much in a comment.
The implementation of that function can be almost identical to the solutions #ThreeFx and #bheklilr proposed... just without the instance part. Here's my rendition of #bheklilr's version:
data Exprs
= Const Double
| Var String --in math we often use char
| Sqrt Exprs --we can take sqrt of anything
| IntPow Exprs Int--the easy case of exponents
| Exp Exprs --e^expr
| Ln Exprs --logarithms
| Mult Exprs Exprs--multiplication
| Add Exprs Exprs
| Neg Exprs
deriving (Eq, Ord, Show, Read)
prettyPrint :: Exprs -> String
prettyPrint e = go 0 e ""
where
go n (Const x) = showParen (n > 10) $ showsPrec 11 x
go n (Var var) = showParen (n > 10) $ showString var
go n (Add l r) = showParen (n > 6) $ go 7 l . showString "+" . go 7 r
go n (Mult l r) = showParen (n > 7) $ go 8 l . showString "*" . go 8 r
go n (Sqrt e) = showParen (n > 10) $ showString "sqrt(" . go n e . showString ")"
Note that all I've done is mechanically rewrite showsPrec as go and wrap it in a convenience function.
Now Read and Show work dandy, and we can get nice pretty-printing:
*SO26169469> Add (Const 7) ( Mult (Const 4) (Add (Sqrt (Var "x")) (Const 3)))
Add (Const 7.0) (Mult (Const 4.0) (Add (Sqrt (Var "x")) (Const 3.0)))
*SO26169469> show it
"Add (Const 7.0) (Mult (Const 4.0) (Add (Sqrt (Var \"x\")) (Const 3.0)))"
*SO26169469> read it :: Exprs
Add (Const 7.0) (Mult (Const 4.0) (Add (Sqrt (Var "x")) (Const 3.0)))
*SO26169469> prettyPrint it
"7.0+4.0*(sqrt(x)+3.0)"
P.S. I don't claim to be any authority; I'm sure many people would support using Show as the other contributors suggest.
Careful: Bad practice ahead!
As Christian pointed out in his comment, show should yield a syntactically valid Haskell construct, which is why overriding show is not considered good practice. You should hava a look at bheklilr's answer or Bakuriu's comments on the question.
Nevertheless, one possible way is to implement the Show instance manually for every constructor:
instance Show Exprs where
show (Const d) = show d
show (Var v) = v
show (Sqrt ex) = "(sqrt(" ++ show ex ++ "))"
..
show (Add e1 e2) = show e1 ++ " + " ++ show e2
..
Sample output:
*Main> Sqrt (Var "x")
(sqrt(x))
*Main> Add (Sqrt (Const 4)) (Var "x")
(sqrt(4.0)) + x

Automatic parentheses in `Show` instances

ghci> show (Left 3)
"Left 3"
ghci> show (Just 0)
"Just 0"
ghci> show (Just (Left 3))
"Just (Left 3)"
How does Haskell automatically put parentheses around nested constructor arguments?
showsPrec :: Int -> a -> ShowS is the function used internally by show to consistently put parentheses around a term.
The Int parameter indicate the precedence of the outer context. If the precedence of the current constructor is greater than the precedence of the context, showParen :: Bool -> ShowS -> ShowS puts parentheses around the constructor. Here an example for a very basic AST :
data Exp = Exp :+: Exp
| Exp :*: Exp
-- The precedence of operators in haskell should match with the AST shown
infixl 6 :+:
infixl 7 :*:
mul_prec, add_prec :: Int
mul_prec = 7
add_prec = 6
instance Show Exp where
showsPrec p (x :+: y) = showParen (p > add_prec) $ showsPrec (add_prec+1) x
. showString " :+: "
. showsPrec (add_prec+1) y
showsPrec p (x :*: y) = showParen (p > mul_prec) $ showsPrec (mul_prec+1) x
. showString " :*: "
. showsPrec (mul_prec+1) y
show t = showsPrec 0 t "" -- Default definition
showsPrec, showString, showParen, etc, act on difference lists (ShowS = String -> String), where the argument is a string appended to the result. Concatenation is done by composition, and conversion to String by application with an empty string.
The show implementation use showsPrec with the lowest precedence, the expression to print, and finally, the end of the string [].

Error: "No instances for (x)..."

Exercise 14.16-17 in Thompson asks me to add the operations of multiplication and (integer) division to the type Expr, which represents a simple language for arithmetic, then define the functions show and eval (evaluates an expression of type Expr) for Expr.
My solution works for each arithmetic operation except division:
data Expr = L Int
| Expr :+ Expr
| Expr :- Expr
| Expr :* Expr
| Expr :/ Expr
instance Num Expr where
(L x) + (L y) = L (x + y)
(L x) - (L y) = L (x - y)
(L x) * (L y) = L (x * y)
instance Eq Expr where
(L x) == (L y) = x == y
instance Show Expr where
show (L n) = show n
show (e1 :+ e2) = "(" ++ show e1 ++ " + " ++ show e2 ++ ")"
show (e1 :- e2) = "(" ++ show e1 ++ " - " ++ show e2 ++ ")"
show (e1 :* e2) = "(" ++ show e1 ++ " * " ++ show e2 ++ ")"
show (e1 :/ e2) = "(" ++ show e1 ++ " / " ++ show e2 ++ ")"
eval :: Expr -> Expr
eval (L n) = L n
eval (e1 :+ e2) = eval e1 + eval e2
eval (e1 :- e2) = eval e1 - eval e2
eval (e1 :* e2) = eval e1 * eval e2
E.g.,
*Main> (L 6 :+ L 7) :- L 4
((6 + 7) - 4)
*Main> it :* L 9
(((6 + 7) - 4) * 9)
*Main> eval it
81
it :: Expr
However, I am running into problems when I try to implement division. I don't understand the error message I receive when I try to compile the following:
instance Integral Expr where
(L x) `div` (L y) = L (x `div` y)
eval (e1 :/ e2) = eval e1 `div` eval e2
This is the error:
Chapter 14.15-27.hs:19:9:
No instances for (Enum Expr, Real Expr)
arising from the superclasses of an instance declaration
at Chapter 14.15-27.hs:19:9-21
Possible fix:
add an instance declaration for (Enum Expr, Real Expr)
In the instance declaration for `Integral Expr'
In the first place, I have no idea why defining div for the data type Expr requires me to define an instance of Enum Expr or Real Expr.
Well, that's the way the Integral typeclass is defined. For information, you can e.g. just type :i Integral into GHCi.
You'll get
class (Real a, Enum a) => Integral a where ...
which means any type a that should be Integral has to be Real and Enum first. C'est la vie.
Note that maybe you've got your types messed up quite a bit. Take a look at
instance Num Expr where
(L x) + (L y) = L (x + y)
(L x) - (L y) = L (x - y)
(L x) * (L y) = L (x * y)
This just allows you to add Expressions if they wrap plain numbers. I'm pretty sure you don't want that.
You want to add arbitrary expressions and you already have a syntax for this. It's just
instance Num Expr where
(+) = (:+)
(-) = (:-)
-- ...
This allows you to write (L 1) + (L 2) with perfectly normal syntax. Likewise, eval should not just reduce expressions but yield a number, and therefore have the type eval :: Expr -> Integer. Division is simple for that matter
eval (a :/ b) = (eval a) `div` (eval b)
which is defined since you just divide numbers.

How can I write an addition of two items in a string representation?

For example I want to add two expressions e1 and e2
toString (Plus e1 e)= ??
I am guessing it would be something like
toString (Plus e1 e)= ((toString e1) ++ "+" ++ (toString e2))
I've made a few assumptions about how Plus is defined. With this code you can just type expr at the ghci prompt and ghci will print out the expression 100-30+100+30.
module Main where
data Expr =
Plus Expr Expr
| Minus Expr Expr
| Value Int
-- This works but is not as Haskell-y as using Show
toString (Plus e1 e2) = toString e1 ++ "+" ++ toString e2
toString (Minus e1 e2) = toString e1 ++ "-" ++ toString e2
toString (Value n) = show n
instance Show Expr where
show (Plus e1 e2) = show e1 ++ "+" ++ show e2
show (Minus e1 e2) = show e1 ++ "-" ++ show e2
show (Value n) = show n
expr = (Plus
(Minus (Value 100) (Value 30))
(Plus (Value 100) (Value 30)))

Resources