A couple of weeks ago I saw these tweets (in Spanish):
As I respect enormously Modesto and Xabi’s opinion and I trust on the skills of a Carlos Ble’s apprentice, I decided to do the kata at home. And as I keep trying to learn F#, that would be the chosen language.
I recommend you to do the kata, is a very interesting one. In case you want to do it, please stop reading now and come back when you have finished it, as I’m going to explain how I solved it.
The kata starts pretty simple: you have to model a combat system between two characters of a RPG game. First you start with simple rules and in every iteration you add a slightly more complex one. At the end of the 4th iteration I ended up with a code like this:
As you can see, I have a list of rules that should be applied when attacking and a list of rules that should be applied when healing. I have a function to traverse a list of rules and apply them, and the rules are quite small and focused in one thing. I was quite happy with the design (remember, I’m learning Functional Programming, I can be totally wrong :) )
The real fun starts in the last iteration. In this iteration a new element of the game is introduced: a Thing. A Thing is an element that has health but nothing else. It can’t heal anyone, it doesn’t belong to any faction and it can’t attack. Is just something that everybody can attack.
Looks like a simple change but introduces quite a bit of complexity in the design. I decided to use a discriminated union to model a Player, so that a player can be either a Character of a Thing.
My first attempt to change the logic of the module was focused in two things:
- Simplify the logic of the different rules, so that they just only need as a parameters the information they will use.
- Put the logic around being a Thing or a Character in the main function
This solution ended up with a function like this:
As you can see, the code is a bit messy. It’s true that the rules now are a bit simpler and only receive as a parameter what they need, but on the other hand we have now three different types of rules that must be traversed in the main function.
So I decided to refactor my solution a bit. What I tried to accomplish was:
- Move the logic around being a Thing or a Character to the rules. Now a rule is the responsible of knowing what to do in each case.
- Simplify the logic of the main function. Now, it just differentiate about an Attack and a Heal, calls the same function to traverse the list of rules (different in each case), and applies the result in a different way.
Here’s the code of the main function:
And here’s the code of a couple of rules:
I prefer this approximation. I don’t have a nested pattern matching in the main function. I only have a list of rules for each case and the rules know what to do in each case. My concerns are:
- What will happen if the types of players grow? Is there any way to generalise the types (i.e. a player that could be attacked, healed, etc)?
- What will happen in the number of parameters of the rules grow? Is a smell that the design is not good enough? Should I introduce a parameter object (well, in F# maybe is a parameter record type :P )?
I’d love to hear your thoughts about the design :)
Author Vicenç García