PurelyFunctional.tv Newsletter 421: Programming monism
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
Design Tip 💡
Programming languages exhibit an unhealthy monism. Monism is a system wherein you believe that a single principle underlies distinct phenomena. For example, in OOP, we might say, "everything is an object." In the lambda calculus, there are only functions. This monism is very useful when you're first exploring how to program in the system. But eventually, you'll want to give more structure to the programming language. As you learn by using the language, you'll want distinct affordances for distinct purposes.
Having a single, unifying mechanism in your language is very nice. It feels great to find a minimal set of constructs for building a Turing-complete system. For example, I think OOP, as manifested in Smalltalk, has such a system. There are only objects, references to objects, and messages. And even messages are objects! We can then build conditionals, loops, dynamic dispatch, and many more sophisticated constructs using only those.
Similarly, McCarthy built Lisp with only a handful of constructs. The universality of the cons cell is very powerful. Lispers built many critical systems with nothing more than the cons, which is a basic way to structure memory. Lisp textbooks were full of cons cell diagrams showing how to build lists, trees, association lists, and more using only conses.
Clojure, however, does not overly worship the cons cell. Instead, it provides multiple custom data structures for structuring data in different ways. Sometimes you want associative data (map). Sometimes you want ordered data (vector). And sometimes you want a big bag of data (set). Rich Hickey broke the spell of monism that had plagued Lisp for half a century.
Is it so weird to think that maybe two things working together are better than a single, universal thing? Or what about three? Or a dozen? I get that, at some point, it's too many to understand. The system becomes complicated. But we can manage three things that work together.
My other favorite example from Clojure is the
split. Why have two constructs? Well,
deftype is made for low-level
objects that are useful for building programming primitives. Think of
building a concurrent reference type similar to atoms. They are not
domain-specific. They are for addressing programming problems, not
defrecord, on the other hand, is meant for
representing domain concepts.
defrecord creates classes that have
equality, hash code, and printing built-in, which are normally needed
for building domain concepts. These two ways to create types have
different affordances and properties to suit two distinct needs. A
monistic language would try to solve both with the same construct.
I think Smalltalk could have used more structure. The original Xerox PARC team did a lot with very little code. I think they could do that because they deeply understood the single construct. As they built iteration after iteration of the Alto, they learned to use different "patterns" for different purposes. But the disciplines of those "patterns" never left PARC. The language did not guide the programmer. A better language would have condensed the patterns into affordances to constrain the programmer to proper usage.
Where is monism useful? I believe that monism, with its single universal construct, is very useful for building the underlying system. A single construct is much easier to optimize than multiple constructs. And it's easier to reason about the behavior of the system at the level of the compiler. However, at some point, you want to provide some structure, if not sugar, for the programmers using your language. That can guide them to write better software and might even cut down learning time. If you're going to expect them to program in a certain way anyway (known as idiomatic), you might as well make those constructs in the language.
Programmer to watch 👀
Currently recording 🎥
I am building a course on how to build a Clojure web stack from scratch.
To build a course, I often start by writing out a very complete guide to the topic. That guide will be published for free on my site. Newsletter subscribers (that means you!) see it first as an exclusive. I often get a lot of great comments and critiques about it. It's easy to fix in text. Once those critiques stop rolling in, I get about prepping for a video recording.
Well, I'm still writing the guide. It's over 6,000 words right now, and I'm not even halfway done. It might make a nice, slim book.
It might be just about ready to start sharing. Stay tuned.
Podcast appearance 📢
I was very honored to speak on DeveloperMelange about the principles of functional programming.
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.
Stay healthy. Wash your hands. Wear a mask. Take care of loved ones.
Clojure Challenge 🤔
Last issue's challenge
- Unique elements - 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
Sometimes you have a list and you want to "promote" one element toward
the front of the list. Essentially, you need to swap the element with
its immediate predecessor (if it exists). Write a function
that takes a predicate and a list. If the predicate is true, the element
should be promoted.
(promote even? [1 3 5 6]) ;=> (1 3 6 5) (promote even? ) ;=> () (promote even? [2 1]) ;=> (2 1) (promote even? [0 2 4 6]) ;=> (0 2 4 6) (promote even? [0 1 2 3 4 5 6]) ;=> (0 2 1 4 3 6 5) (promote even? [1 2 2 2 2]) ;=> (2 2 2 2 1)
Thanks to Enzzo Cavallo for the challenge idea!
Please submit your design process as comments to this gist. Discussion is welcome.