I really like this style of post comparing the tradeoffs of several designs for the same problem. I would love to see a hundred more posts like it.
I do have to gripe that it contains an error:
Unfortunately, this design is flawed. The equality operator is broken. Depending on the way you assemble the expressions, two equivalent expressions may have very different AST
let x = add (add a (multiply b 2)) c -- (a + b * 2) + c
let y = add a (add (multiply b 2) c) -- a + (b * 2 + c)
The author is correct in the general case of “we’ve created an algebra and these two equivalent statements are unequal”, but in the problem domain of money and currency conversions, addition is not commutative. “1 USD + 1 EUR -> x USD” is not equivalent to “1 EUR + 1 USD -> y EUR -> z USD”. In currency conversion, even ignoring fees and rounding, converting from currency A to currency B and converting back to A is not the identity function. The rates for A -> B and B -> A are not inverses.
So the author is incorrect in this example. Two better examples: the trees multiply a 2 and multiply 2 a, as well as add a b and add b a where a and b are of the same currency, will be incorrectly considered to not be equivalent.
Thanks a lot for the nice comments as well as the invitation.
Now, regarding the error (and ignoring the rounding errors and the fees - same assumptions) and considering that rates are not inverses (a nice point indeed), I do not understand why the addition would not be commutative (but I might have overlooked something here).
The addition in the post only creates an AST. The AST will later be evaluated with the function evalMoneyIn which takes as input the rate curves and the destination currency. So the order in which the terms appear in the AST do not impact the result in the sense that the same rate curves will be used.
In the second example of MoneyBag, the order is not preserved either, and this is what allows to simplify the MoneyBag when two amounts of the same currency are added together.
Yes indeed, implementing the valid Eq instance for MoneyExpr would certainly be an interesting task. I did not actually try it, but thought about it.
I think it goes through simplifying the expression somehow, which ends up looking like the MoneyBag: every term in the top addition, sort by currency, and then match with a zip or equivalent. There might be a better way though.
I really like this style of post comparing the tradeoffs of several designs for the same problem. I would love to see a hundred more posts like it.
I do have to gripe that it contains an error:
The author is correct in the general case of “we’ve created an algebra and these two equivalent statements are unequal”, but in the problem domain of money and currency conversions, addition is not commutative. “1 USD + 1 EUR -> x USD” is not equivalent to “1 EUR + 1 USD -> y EUR -> z USD”. In currency conversion, even ignoring fees and rounding, converting from currency A to currency B and converting back to A is not the identity function. The rates for A -> B and B -> A are not inverses.
So the author is incorrect in this example. Two better examples: the trees
multiply a 2
andmultiply 2 a
, as well asadd a b
andadd b a
wherea
andb
are of the same currency, will be incorrectly considered to not be equivalent.Thanks a lot for the nice comments as well as the invitation.
Now, regarding the error (and ignoring the rounding errors and the fees - same assumptions) and considering that rates are not inverses (a nice point indeed), I do not understand why the addition would not be commutative (but I might have overlooked something here).
The addition in the post only creates an AST. The AST will later be evaluated with the function
evalMoneyIn
which takes as input the rate curves and the destination currency. So the order in which the terms appear in the AST do not impact the result in the sense that the same rate curves will be used.In the second example of
MoneyBag
, the order is not preserved either, and this is what allows to simplify the MoneyBag when two amounts of the same currency are added together.Ah, I misread how
evalMoneyIn
worked. I thought addition was converting one of the two arguments to the other’s currency. Thanks for the correction.It would be an interesting exercise for the reader to implement a valid
Eq
instance forMoneyExpr
.Yes indeed, implementing the valid Eq instance for MoneyExpr would certainly be an interesting task. I did not actually try it, but thought about it.
I think it goes through simplifying the expression somehow, which ends up looking like the
MoneyBag
: every term in the top addition, sort by currency, and then match with a zip or equivalent. There might be a better way though.Just posted a related story