#1. The decision about dropping the parentheses depends on which side of the operator we're on. So, here's the new prettyShow -- changes in bold:
prettyShow :: (Show a, Num a) => SymbolicManip a -> String -- Show a number or symbol as a bare number or serial prettyShow (Number x) = show x prettyShow (Symbol x) = x prettyShow (BinaryArith op a b) = let pa = maybeLeftParen a op pb = maybeRightParen b op pop = op2str op in pa ++ pop ++ pb prettyShow (UnaryArith opstr a) = opstr ++ "(" ++ show a ++ ")"
Now, for the helper functions. Let's start with handling the left-paren:
maybeLeftParen :: (Show a, Num a) => SymbolicManip a -> Op -> String maybeLeftParen x@(BinaryArith op1 _ _) op | op1 == op && isRightAssociative op = "(" ++ prettyShow x ++ ")" | (opPrio op1) < (opPrio op) = "(" ++ prettyShow x ++ ")" | otherwise = prettyShow x maybeLeftParen x _ = simpleParen x
For this to work, we need to know each operator's priority, and whether the operator is right-associative. These are trivial (the priorities are taken from Haskell's definitions; type :i (+) in ghci, for example):
opPrio :: Op -> Int opPrio Plus = 6 opPrio Minus = 6 opPrio Mul = 7 opPrio Div = 7 opPrio Pow = 8 isRightAssociative :: Op -> Bool isRightAssociatove Pow = True isRightAssociative _ = False
Finally, for handling parenthesis to the right of the operator, we have this:
maybeRightParen :: (Show a, Num a) => SymbolicManip a -> Op -> String maybeRightParen x@(BinaryArith op1 _ _) op | prio1 < prio = "(" ++ prettyShow x ++ ")" | prio1 == prio && opIsMinusOrDiv = "(" ++ prettyShow x ++ ")" | otherwise = prettyShow x where prio1 = opPrio op1 prio = opPrio op opIsMinusOrDiv = (op == Minus) || (op == Div) maybeRightParen x _ = simpleParen x