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 undefined
s, 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.