PurelyFunctional.tv Newsletter 403: the timeless in an unstable domain
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
Clojure Tip 💡
the timeless in an unstable domain
Natural domains are nice. Science has benefited from the mysterious capacity for natural systems to be described by elegant, timeless laws. Building a domain model can benefit in the same way. For instance, if you're writing a physics simulator, you can be sure the notions of force, mass, distance, and time will not only be easily modeled, but that the model won't need to change once you find a good one. We can find a representation of reality that captures truth and is timeless.
But what if our domains are artificial? A great example is the shifting, ever-changing policies of your company. It would be hard to have a description of them as elegant as F=ma. Finding truth (a good description that captures the relevant details) is hard. But further, how can you capture anything timeless? Well, I think there's always a lower level where a timeless model can be found.
First, let's define timeless. A timeless model doesn't have to change despite changes to the domain. A timeless model has something of the quality of Newton's three laws of motion. The truth of it is well-tested. But even as new things are added to classical mechanics, such as fluid dynamics, the three laws remain unchanged. Features are added, but this model is timeless. Timelessness is a product of many factors, including truth, the stability of the domain, and the scope of the model.
Let's take another example. We can imagine writing a module for
validating email addresses. We just need to write a predicate called
email/valid? that determines if it's a good email or not. We can
start quite simply and evolve it over time. Eventually, as we capture
more and more truth, the model will resemble the email address RFC to a
finer and finer detail. When it converges on the same set of strings as
the RFC (with perhaps a few de facto practices that aren't in the
RFC), we could declare the library finished.
The library has truth in that it accurately captures the current
understanding of email addresses. We purposefully scoped the library
to a well-defined problem. Imagine if we also included other forms of
identification such as valid Twitter handles or other social media
services, perhaps calling it
id/valid?. We are biting off a big chunk
that includes a lot of changes in the future. So the scope helps. And
the library would also be timeless. We could use the library
confidently for years without having to touch it.
Or let's imagine a simple physics simulator that only had to deal with masses of exactly 10kg. You wouldn't have to even have a notion of mass and instead just hard-code things. And what if there were no forces in the simulator? Everything would just move in straight lines. You could even hard-code the trajectories. Notice that you could still model the small physics simulation you've got to a high degree of truth, but if anything changed, the code would have to change. Those hard-coded parts would have to change as the domain changed. But if we had the notion of mass and force, we lose no truth, but add timelessness. The physics engine would not need to change. We just feed it different initial conditions.
Those are good illustrations of the idea. But we're relying on the fact that email addresses are stable and well-described in the RFC. And we're relying on the fact that mechanics was already solved by Isaac Newton. What if we have to model the rat's nest of laws regarding voter eligibility for the US presidential election? Well, it turns out I was faced with that issue, so I've thought a lot about it.
Just a refresher: the US presidential election is actually not a national election. It's 50 state elections that happen on the same day. And each state has different rules for who is eligible to vote and when and how. Most states require you to register with your current address so they know what district you are voting in. But some states don't require it. Some elections need you to be registered as a Democrat or Republican. Some states let you mail in votes, but it might depend on your age. I mean, it's really a mess if you look at the whole country. What's more, the laws are constantly changing.
This is typical of artificial systems like legal regulations or business policies. They can change at any moment. And they're not always modeled in a logical way. They agglutinate rules a little at a time. And those rules are not designed as much as compromised into existence.
So, in such a system, is there any hope? I argue that there is. I've talked about this problem in my podcast (here's the episode in case you like to listen).
First, we can scope down the problem. We can just ask a simple question like "can person P vote in election V?" We don't have to come up with a rational model of the laws. We just have to answer yes or no to a question, which is feasible.
Second, we could get a truth of the moment. We snapshot the laws as they exist today. We code it as a big nested conditional. It won't be pretty, but it would work. I knew people that could cite all the rules of each state. Just sit down with them, go state-by-state, and code it up.
That just leaves timelessness. Is it achievable? With all of the changes to the laws, and the big mess of rules, can you ever find a model that won't require you to change the hard-coded conditionals? Well, I think so. You just need to dig deeper, underneath the mess, and look for what you sense won't change. In this case, we already have the answer: the signature of the function is a good start. Remember, we were trying to answer the question "can person P vote in election V?". That translates into this signature:
(defn can-vote? [person election]) ;;=> returns true or false
There are still unknowns, such as what exactly we have in
election. But we can imagine that we could find a set of information
in the two arguments that will be able to implement this function
correctly. The implementation isn't timeless, but the signature is
timeless. The signature is well-scoped, captures the truth of the
question, and will work for all time. We've done it! We've found a
bedrock that we won't have to change, ever. Now we can build on top of
it. But this email is super long, so it will have to wait till next
Book update 👃
Folks, the end of my work on the book is so close I can smell it. I've completed the layout and it is looking sweet! I just have a few more exercises to add and it will be out of my hands. Just some proofing, indexing, and production, and it will be ready to print.
Plus, I'm really excited about the person who will write my foreword. But shhh! It's a surprise. For now.
You can buy Grokking Simplicity and use the coupon code TSSIMPLICITY for 50% off. You can buy it now and it will be shipped to you when it's printed. Thanks to those of you who have already purchased 😘
I'm also really excited because Manning sent it out to reviewers. They gave me great revisions (already done :), but more importantly, they liked the book and said fantastic, glowing things about it. Let's hope that is a good prediction of what the wider world will think.
Of course, I must apologize for the absence of an email last week. I was deep in layout for the book, working late each night 🥱, and kept putting it off until, oops, it's next week. Lost week, I guess! Enjoy it this week.
I recorded a new episode last week about John Backus's 1977 Turing Award Lecture paper Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs. It's a long one. But this paper is a great glimpse at a vision of programming from a luminary in our field.
Quarantine 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.
Also, if you just want to subscribe for a paid membership, I have opened them back up for the moment. Register here.
Stay healthy. Wash your hands. Wear a mask. Take care of loved ones.
Clojure Challenge 🤔
Last issue's challenge
- Most frequent element - submissions
Please do participate in the discussion at the submission links above. It's active and it's a great way to get comments on your code.
This week's challenge
Sort by content
In this task, we are sorting a heterogeneous list of elements. The elements are either single numbers or sequences of numbers. We should sort them by numeric content, like this:
(sort-by-content [4 5 3 2 1]) ;=> [1 2 3 4 5] ;; we sort sequences by their first element, then by second element, then by third, etc (sort-by-content [[2 3] [0 9] [-1 3 4] [0 3]]) ;=> [[-1 3 4] [0 3] [0 9] [2 3]] ;; we sort numbers and sequences together as if numbers were sequences of length 1 (sort-by-content [5 [4 5] 3 1 [0 2 3]]) ;=> [[0 2 3] 1 3 [4 5] 5] ;; but numbers sort before sequences when the number is the same as the first element (sort-by-content [ 1]) ;=> [1 ]
Please submit your solutions as comments to this gist. Discussion is welcome.