Reputation: 3
I would like to re-run a function back on a pattern after that same function has been evaluated on parameters inside a Data constructor being passed to the function. For example
func (Plus a b) = func (Plus (func a) (func b))
Note that a
and func a
are of the same type. When I try to call something like this, the program gets hung up, and I think what's happening is the pattern is getting indefinitely matched against itself before evaluating the inner (func a)
and (func b)
, which would otherwise match it to a different pattern. I have tried using something like
func (Plus a b) = func (Plus newa newb)
where newa = func a
newb = func b
to try and force the evaluation of func a
and func b
first, but this does not work. Is what I am trying to do even possible?
Thanks.
Upvotes: 0
Views: 148
Reputation: 60463
You do get into a loop where you match Plus
over and over, but what do you expect?
func (Plus a b)
= func (Plus (func a) (func b))
= func (Plus (func (func a)) (func (func b)))
= func (Plus (func (func (func a))) (func (func (func b))))
The problem is that you are essentially saying, "to evaluate func
on a Plus
constructor, evaluate func
on a Plus
constructor".
What does the rest of func
look like? Your approach could, in principle, work if the whole definition were something like:
func (Plus (Lit n) (Lit m)) = Lit (n + m)
func (Plus a b) = func (Plus (func a) (func b))
Here if func a
and func b
eventually reduce to Lit
, then the first pattern will match and the call will terminate. But I would hesitate to write a function this way, because how do you know that repeatedly calling func
on an argument will eventually converge to Lit
? There will probably be a case where it won't.
I suspect you are writing a symbolic evaluator. The better way to do this is to come up with a normal form for expressions, and then have your reducer reduce to the normal form. A normal form is a unique representation that you can always reduce an expression to. It sometimes takes some cunning to come up with a good normal form. But let's say your expression type were:
data Expr = Lit Int | Var String | Plus Expr Expr
For this, an example normal form might be:
data ExprNF = ExprNF Int [String]
which represents a constant plus a sum of variables in sorted order (keep it sorted so that equivalent expressions always compare equal). So if your expression were
1 + (x + 2) + (3 + 6) + (x + (y + x))
It would become the normal form:
ExprNF 10 ["x","x","x","y"]
Your reducer func :: Expr -> ExprNF
computes the normal form recursively, and there is no need to repeatedly call it in hopes of convergence.
func :: Expr -> ExprNF
func (Lit n) = ExprNF n []
func (Var s) = ExprNF 0 [s]
func (Plus a b) = case (func a, func b) of
(ExprNF n vs, ExprNF n' vs') -> ExprNF (n + n') (sort (vs ++ vs'))
We never have to reduce any node in the tree more than once, because we get a normal form right away.
Upvotes: 3