<div dir="ltr">Hello all,<div><br></div><div>I'm learning about data types and type classes and tried implementing a simple symbolic expression evaluator, but generalised for non-numeric data types. </div><div><br></div><div><div>data GenExpr a = Atom a | Sum (GenExpr a) (GenExpr a) | Prod (GenExpr a) (GenExpr a) | Neg (GenExpr a)</div></div><div><br></div><div>What I am hoping to do is write a general evaluation function which can some GenExp expression and evaluate it. For example, giving it a GenExp Integer would evaluate it according to normal arighmetic, GenExp Bool would be evaluated using Boolean algebra etc. I've declared explicit type classes for types that can be summed, multiplied and negated:</div><div><br></div><div><div>class HasSum a where (.+.) :: a -> a -> a</div><div>class HasProd a where (.*.) :: a -> a -> a</div><div>class HasNeg a where (.-) :: a -> a</div><div><br></div><div>instance HasSum Integer where (.+.) = (+)</div><div>instance HasProd Integer where (.*.) = (*)</div><div>instance HasNeg Integer where (.-) = negate</div><div><br></div><div>instance HasSum Bool where (.+.) = (||)</div><div>instance HasProd Bool where (.*.) = (&&)</div><div>instance HasNeg Bool where (.-) = not</div></div><div><br></div><div>-- Evaluate generalised expression</div><div><div>genEval :: (HasSum a, HasProd a, HasNeg a) => GenExpr a -> a</div><div>genEval (Atom a) = a</div><div>genEval (Sum ge1 ge2) = genEval ge1 .+. genEval ge2</div><div>genEval (Prod ge1 ge2) = genEval ge1 .*. genEval ge2</div><div>genEval (Neg ge) = (.-) $ genEval ge</div></div><div><br></div><div><br></div><div>However, I would like it to work with types that don't support all three operations, e.g. natural numbers which don't have a negation operation or strings that only have concatenation as addition:</div><div><br></div><div><div>instance HasSum Nat where (.+.) = (+)</div><div>instance HasProd Nat where (.*.) = (*)</div><div><br></div><div>instance HasSum String where (.+.) = (++)</div></div><div><br></div><div>But these wouldn't be suitable inputs for genEval because they don't fulfil all three type constraints. The only solution I can think of is writing separate genEvalNat and genEvalString functions, which kind of defeats the purpose of the generalisation.</div><div>Is there a more elegant way to solve this problem? </div><div>Thank you in advance!</div><div><br></div><div>Dima</div></div>