PurelyFunctional.tv Newsletter 421: Programming monism

Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.

Issue 421 - April 05, 2021 ยท Archives ยท Subscribe

Design Tip ๐Ÿ’ก

Programming monism

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 defrecord/deftype 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 domain problems. 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 ๐Ÿ‘€

I think Jessica Kerr is one of the most important voices in the practice of software engineering today. Get on her newsletter.

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

Issue 420

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

Element promotion

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 promote that takes a predicate and a list. If the predicate is true, the element should be promoted.

Examples

(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.

Rock on!
Eric Normand