Have you ever found yourself writing a massive nested if statement and thinking, 🤮? If so, this might be the post for you.
First, lets talk about the problem. It's not uncommon when developing systems to have to implement some business logic that can be represented abstractly by a flow chart or decision tree. Below is an example of a fairly simple tree/chart to use as an example. Obviously it's likely that your own problem could be much more complicated that this, but hopefully it will serve to illustrate the point:
This decision tree represents the business logic behind which type of apostrophe you should use (or not) in any given situation. It isn't uncommon for Domain Experts to illustrate their business logic through the use of a decision tree similar to this.
Decision trees can be directly mapped to my preffered way of displaying this type of information, a decision table. In a decision table, we take the question nodes in the tree and put them along the top row. Then we create an extra column on the right of the table and fill in the "actions" we should take, these are the leaf nodes in our decision tree. Then we fill in all of the different possibile ansers to our question node, in our case true (✔️) false (❌) or either (🤷).
Decision tables (and trees) are composable, so it's possible to nest them into smaller subtables (or trees). Here are three tables that represent the decision tree above.
Plural? | Single letter? | Abbreviation? | |
---|---|---|---|
❌ | 🤷 | 🤷 | See Possessive table |
✔️ | ✔️ | 🤷 | Apostrophe + s |
✔️ | ❌ | ✔️ | Apostrophe + s |
✔️ | ❌ | ❌ | No Apostrophe |
Plural table
Possessive? | Is it "it"? | "it is" or "it has"? | name ending in s? | plural name? | ends in s? | |
---|---|---|---|---|---|---|
❌ | 🤷 | 🤷 | 🤷 | 🤷 | 🤷 | See Contraction table |
✔️ | ✔️ | ✔️ | 🤷 | 🤷 | 🤷 | Apostrophe + s |
✔️ | ✔️ | ❌ | 🤷 | 🤷 | 🤷 | No Apostrophe |
✔️ | ❌ | 🤷 | ✔️ | 🤷 | 🤷 | Apostrophe at end |
✔️ | ❌ | 🤷 | ❌ | ✔️ | 🤷 | Apostrophe at end |
✔️ | ❌ | 🤷 | ❌ | ❌ | ✔️ | Apostrophe at end |
✔️ | ❌ | 🤷 | ❌ | ❌ | ❌ | Apostrophe + s |
Possessive table
Contraction? | Pronoun? | with a verb? | |
---|---|---|---|
❌ | 🤷 | 🤷 | No Apostrophe |
✔️ | ✔️ | ✔️ | Use Apostrophe |
✔️ | ❌ | 🤷 | Use Apostrophe |
✔️ | ✔️ | ❌ | No Apostrophe |
Contraction table
If your working with a Domain Expert who likes to use decision trees consider asking them to switch over to using tables instead.
Okay, so we've got the logic for our Domain, now how do we implement this in code? We could choose to write a fairly large nested if statement. Or alternatively, if we're given a tree, why not represent this information as a tree? We could create a tree structure with nodes, edges and leaves and model this directly.
These could would work, but have a couple of issues. If our Domain Expert comes back and tells us that the business logic has changed and provides a new tree. It's not so easy to translate the new logic into the code with either of these two methods. There's no direct mapping and in general it can be hard to look at a large nested if statement (assuming you can fit on a single screen) and easily determin the logic behind it.
Pattern matching FTW!
Using pattern matching is my prefered solution to this problem. We can take our decision table(s) and directly map them to code in a way that looks and feels much very similar to the original information.
Lets take the tables above and illustrate them as an example. I'm going to use Rust because it has a great type system and pattern matching built-in. Most languages now support this kind of syntax (I don't think JavaScript does... 🤨).
The whitespace formatting I've chosen is here to help illustrate how closely linked the two concepts are and to help read the table, it's a personal preference thing though.
If you're using a language with a good type system and compiler, then the added benefit of using pattern matching is that you get compile time checking of your logic. Unlike a series of if statements, you'll be warned if one of your patterns (table rows) are unreachable or if you've missed any cases. In C# you'll get a compiler warning with a nice hint suggesting cases that you've missed. It's not uncommon to include a catch-all case at the end where you can decide what to - perhaps throw an error.
That's all really! Let me know in the comments what you think of this patern, have you used it before? Would you use it? Are there any other solutions you've found that work better?
Top comments (0)