PurelyFunctional.tv Newsletter 450: Runnable specifications
Design Idea 💡
This essay continues the series about Domain Modeling.
In the last issue, I talked about seeking precision and simplicity in our models. Precision means it describes the model we want. And simplicity means it finds a general and minimally sufficient set of operations. But how do we know it is precise? Even a simple model can have complex behavior. It turns out that we have a potent tool at our disposal for knowing if our model does what we want.
Isaac Newton created one of the most elegant domain models in history: his laws of motion. After conceiving them, he had to painstakingly calculate examples by hand to verify that they could describe real-world situations. Imagine if Newton had had the kind of computer we have on our desks right now. He could have tested the model in hours, not months.
We might not have Newton's brilliance, but we do have the computer. We can build a toy model of the solar system and watch the planets orbit the sun at 120 frames per second. We can make simple physics simulations using only his equations and some geometry. In other words, we can supplement our lack of genius with raw compute power.
Here's the process: We write signatures for the operations we want. That's part of the specification. And when we are satisfied that we have the right ones, we write bodies for the signatures in the minimal set (you implement the other operations in terms of the minimal set). Now we have a runnable specification.
You won't ship these functions to production. You write them to answer questions. If the question is "What does this look like?" you write a version that draws to the screen. If the question is "Does this maintain all invariants?" you can write an in-memory version that you can test with property-based testing.
Those questions can help you learn if you're missing something. But remember, these are not the final implementations. They won't have all of the properties you want. You build them to probe your model. If you find problems, you can debug them before you bring on the complexity of your implementation choices.
And what's more, once you've debugged it, you can use that model to validate your implementation. Your model becomes the oracle saying how your implementation should behave.
Book update 📘
I wrote Grokking Simplicity because I couldn't find a book for beginner functional programmers. People kept asking what book I recommended and I had no answer. Through countless discussions about what functional programming is and what its value is, I refined the idea of actions, calculations, and data. That idea became the core of the book. I've received lots of messages from people saying that idea was instrumental in their understanding of functional programming. It's very rewarding.
In my new episode, How to avoid premature optimization?, I talk about how we can avoid premature optimization by spending more time thinking about the domain before we choose an implementation.
Pandemic update 😷
I know a lot of people are going through tougher times than I am. If you, for any reason, can't afford my courses, and you think the courses will help you, please hit reply and I will set you up. It's a small gesture I can make, but it might help.
I don't want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven't been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can't afford it, just hit reply and I'll set you up. Keeping busy can keep us sane.
Stay healthy. Wash your hands. Wear a mask. Get vaccinated if you can. Take care of loved ones.
Clojure Challenge 🤔
Last issue's challenge
- Phrase maximization - Submissions
This week's challenge
No challenge this week :)