Simple calculator using TDD in F# (part-3)


In this part, I will try to solve the problems that I have created in previous parts (part-2 and part-1). First of all, I will add the ability to return int, float and even errors a a result of evaluating RPN expression. So, I add a type, directly in the RpnCalculator module, called “RpnResult” and my calculate method now looks like this:

03-01

With this change in place, the test project fails to build as I assumed “int” return type previously. It is fairly easy to fix, by simply adding “Integer” in front of every expected “int” return type. I am not going to show that step. But, this link should turn out to be helpful if you fail to understand how to make the test project build again. Needless to mention, all the tests are still green.

I am adding a clever little test case that will make sure that I can operate on “int” and “float” numbers simultaneously and can return proper result from the RpnCalculator. Here is how it looks:

03-02

Surprisingly, current implementation passes almost all the test cases for this test, except for “2 + 1.4 = 3.14” and “2.6 + 3.7 = 6.3”. It took me quite a while to find out which should be surprising- the failing tests? or, the passing ones?

Anyway, in summary, the reason for failure is- floating point numbers are handled a bit differently than we naturally assume about numbers. That’s why, although 1f + 2.14f == 3.14f , but 2f + 1.14f != 3.14f . The reason behind this is beyond the scope of this blog. This is another late realization that- I should really not be using floating point numbers. Decimal is always a safer choice if you can use them. Looks like I can. So, lets jump in and change all our float types to decimal. This is not a difficult task, but a time consuming one. So, I am not going to document it here. But, for interested readers, you can find the changes I made in this commit. And, with this change from float to decimal, all our tests green magically.

I forgot to add two more (actually, one) trivial cases. Let’s add them in the TrivialTests.fs file in the RpnCalculator.Tests project. Here they are:

03-03

And, I can easily make them pass by changing the implementation of evaluateRpnExpr method like this:

03-04

Well, it is about time to force the real RpnCalculator implementation with more than two operands and other supported operations. Best way to do that would be to right some tests. I am getting either lazy or smart to right too many tests. I am adding a new file the in the test project with just one theory. Looks like the following cleverly written test should be sufficient to force a “real” implementation of the calculate method for more than two operands:

03-05

Actually, implementing a function that evaluates RPN expression turns out to be a quite simple one as I already have all the types and helper functions defined. So, following the worked out example in wiki here, my implementation becomes:

03-06

Looks pretty simple for a fully operational Rpn calculator. But, this is what makes all the tests pass. So, I am happy with that. I have changed the signature of evaluateRpnExpr and also made it private. The whole calculation looks much cleaner than the previous one. Let’s leave it this way in this part. We may refactor/reorganize it further once we add more functionalities to it.

Useful links