Iteration 9 - custom functions

Welcome to the 9th iteration of WebCalc. Previously I managed to extract billing module from the app module and started differentiating users from one another. This time I’ll enable the users to define their own functions. To IDEs then.

But first I must apologize for what I’ve done last time. I coupled my User class with Spring’s UserDetails. It’s obvious to me now, in my face even. Apparently, it wasn’t back then. What I should have done is to create my User class free of any Spring influences and then create a SpringUserAdapter class that implements Spring’s UserDetails and keeps internally an instance of my User class. That way my application can use User class wherever it needs to oblivious of Spring’s existence, but when necessary it has a mechanism to talk to Spring in its own language. Luckily the mistake is easy to fix, though it takes a few changes, see commit 5230203c.

From now on I’ll also be skipping all upgrade information, it’s necessary but doesn’t bring anything in this series. Still, I’ll be doing the upgrades. This time it was a bigger one, after upgrading Gradle I also switched to the modern plugins DSL and unified the script where possible, see commit c590bccb if you’re interested.

Now to the actual coding exercise. At the end I want my users to be able to define their own functions. My example would be calculating the area of a circle. In reverse polish notation that would look like this: ^2 π *. This assumes, that the radius of the circle is already on top of the stack I’m using. To define a function like this I need one more element, functions name. I’ll just prefix functions definition with it. Now I can write my test:

As expected, it fails. I’ll be going baby steps, I’ll try to stay almost constantly in green and only push the functionality to desired places by adding new tests. The answer to the question “what is the simplest thing that could possibly work” with my currently red test is this:

That satisfies the tests, but just them. I’m now left with two calls that need to be passed down to the Calculator. The problem is that they need to go together. The only way to verify that defining custom function works is to first define a function and then do calculations with it. Otherwise, I’d need to test some internal state of Calculator and thus couple my tests to the chosen implementation. That would be wrong. The test for the Calculator itself looks like this:

It doesn’t even compile, so:

Now it fails because “circle_area” function is not defined. No surprise here. Again, using baby steps I’m going to hardcode the result just to make the test pass.

I think I can now fix the CalculatorController and just call Calculator.

I just used obvious implementation here and pushed all the responsibility to the Calculator. I don’t expect to revisit the controller again in this iteration, but we’ll see.

Now the real troubles begin. If I take a look at the definition of my function, ^2 π *, I see two issues. First of all, I don’t have a square (^2) function, but even more troublesome is the fact that it takes just one argument! So far all my functions were taking 2 arguments. So refactoring is waiting ahead. The second problem I’ll have is π. It’s not a regular digit, I cannot just call new BigDecimal("π"). What I can do is to use a fact that Java has a constant Math.PI and make π a 0-argument function. That would align very well with what I need to do anyway with ^2 function.

Like mentioned already my main problem now is that in Calculator my code assumes that the functions are always taking 2 arguments. Let’s remove that assumption and let each function decide on its own how many arguments it takes. For now, I don’t need any new tests, I’ll be refactoring in green.

Now all my function get the Stack and take whatever they need, all by themselves. That allows me to try to implement a π function. The test is simple:

The implementation even simpler:

Square function comes next, it’s also trivial:

I have now all the individual pieces I need to calculate my circle_area function. I’m also getting an actual definition of circle_area function through defineCustomFunction. I could now replace circle_area in the input string with the definition, and it should work.

Indeed, it does the trick. However, now my integration test failed. After digging deeper I found out, that my CalculatorController.defineCustomFunction() function is getting as a parameter this: circle_area ^2 ? *, it looks like π character it not passed through properly. I must say it’s interesting and unexpected.

The error took a bit of time to be found, but is, as usual, on me. What I did wrong this time is forgetting to set character encoding in Rest Assured. By default, it uses “ISO-8859-1” which doesn’t contain π. The fix is easy:

To push me further with defining and using custom functions I need another example of a function. This follows ZOMBIES approach, I support currently just one custom function and now I want to support many. To keep it simple the function with its definition would be square_area ^2:

It now works, but I don’t like the solution. It’s not only about that it’s wrong. If I’d define two functions and in one I’d use the other, successful calculation would then depend on the order in which customFunctions would return me the entries. There’s something else. But let’s first try to create a test for the scenario I described.

It fails, I was right, and I need to do something about it.

What bothers me is that there’s currently no way of executing partial computation. Let’s take an example: 2 3 + circle_area 3 *. If I had ignored replacing a function name with its definition, after applying a few steps I would arrive at a place where I’d have 5 on the stack, and I’d have circle_area 3 * still to process. It is now that I’d like to take the definition of circle_area and calculate it using the stack I got so far. I do not seem to have a function in my Calculator class that could do it.

After looking closely at my eval function I can see that it does two things. One is parsing and tokenizing the input, creating a new stack and formatting the result, so it’s translating between input/output format and the structures I need inside Calculator. The other one is looping through the tokens and performing the actual computation. I always knew that there’s something wrong with it. It looks to me that if I separate those two responsibilities, I should get a function I need.

It looks better, but the number of parameters I had to pass worries me a bit. I’ll try to deal with that later. I can finally stop replacing function names with their definitions and just try to evaluate them once I encounter them, like so:

It looks far better now, I’m using custom function only if I encounter them, and the problem with the order of replacing names with definitions of custom functions is gone, the test verifying that is green.

I believe I have now a fully functioning system with the support of defining custom functions. Time to make it right. Let’s start with something small. All functions take their parameters from the stack by themselves, but they let someone else put the result back on the stack, that’s not really correct.

The second thing that bothers me for some time already is the inner eval. Specifically, each token can be one of a number, a predefined function or a custom function. There should be a switch or a polymorphic call, but I have a combination of a try/catch and an if. My first attempt with an enum:

I didn’t even use the TokenType yet, but I already don’t like where this is going. detect required me already to:

  1. pass customFunctions map into it,
  2. make formatter static,
  3. duplicate logic from parse function.

All three together smell of feature envy, they refer back to Calculator class itself too often. Besides, my code is now WET. I could try to DRY it out a bit, but looking at it I’m getting an idea of a better approach.

Now eval() looks absolutely hideous, but you probably already know where I’m going with that. I also didn’t get rid of this if/try/catch combination, but at least it doesn’t pollute eval function. What TokenType needs now is a method, that will take everything that is necessary for all three implementations to do their jobs. Then all the statements from ifs will go to their respective classes. But before I could do that I need one more thing. There seem to be three objects constantly being passed around together: stack, userId and maxFractionDigits. They seem to form some EvaluationContext.

Now I can change all implementations of TokenType and add an apply function. I’m following Java naming convention from Function interface here.

Now it looks far better, if series trying to figure out the class is gone along with class casting. Now it looks like function belongs to the PredefinedFunction class and parse to the Number class, let’s move them.

One last tiny thing. Custom functions are still kept as full strings and are tokenized on every execution. I don’t like that.

Now I’m satisfied and all my tests are still green, good. A small comment, all those refactorings I did are to my liking. They seem to be going slightly into the direction of functional programming. It’s honestly a matter of taste. The problems with the design I was trying to fix were also not critical, I could live with them in the code. In real life, I’d probably do some of them, but later, once they really start to bug me. But as this series is about learning and showing how to do things, taking care of the design, etc. I decided to do all those refactorings. The tests helped me a lot, at no point in time I was worried that I would break something without noticing.

That’s it this time. In the next iteration, I’ll try to look into Java libraries. That was also one of my goal for this series. I might not have that much code yet, but it should be enough to try and build the project using libraries. That also means that I’ll not be really coding, only messing around with building the project. See you next time!