PurelyFunctional.tv Newsletter 351: Clojure tool: babashka

Issue 351 - November 11, 2019 ยท Archives ยท Subscribe

Clojure Tool ๐Ÿ”จ


It's Clojure for your Bash scripts. It uses Graal to compile Clojure to native, so it starts up fast. It can run Clojure commands over lines of input, so it works with the Unix philosophy.

You should know that I'm just starting to experiment with this tool, but I already love the potential. I'm not afraid of Bash, but let's be honest: it's not the best language for complex data structure manipulation. Clojure fits that bill, and babashka seems to be tuned to slip right into an existing Bash script where Bash isn't so great.

Follow-up ๐Ÿ™ƒ

In the last couple of issues, I talked about monoids and associativity. I mentioned that clojure.core/merge was a monoid but that it doesn't follow the monoid pattern.

An astute reader (and awesome community member) mentions that the order of arguments to merge does matter. For instance:

=> (merge {:a 1} {:a 2})
{:a 2}
=> (merge {:a 2} {:a 1})
{:a 1}

Same arguments, different order.

They are right that the argument order matters, but this doesn't affect its status as a monoid. This is a common misunderstanding between associativity and commutativity. I received a couple of monoid implementations that did not maintain argument order.

To understand associativity vs commutativity, let's review.

Monoids are associative operations that have an identity. Associativity means the order of operations doesn't matter. That is, where you put the parentheses doesn't change the output.

=> (merge {:a 1} (merge {:a 2} {:a 3}))
{:a 3}
=> (merge (merge {:a 1} {:a 2}) {:a 3})
{:a 3}

More abstractly:

(= (f a (f b c))
   (f (f a b) c))

With Clojure's monoid pattern, you can write:

(f a b c)

Notice we did not change the order of a b c. Associativity means the order of operations doesn't matter, but the order of arguments might matter.

Commutativity means that an operation's argument order doesn't matter. For instance:

=> (+ 1 2 3)
=> (+ 2 1 3)

This example is confusing because + is both associative and commutative. It's hard to find operations that are commutative but not associative. But we can easily make one.

(defn average [a b]
  (/ (+ a b) 2.0))

=> (average 10 7)
=> (average 7 10)

That means it's commutative (argument order doesn't matter). But you can't use this two-argument average to do an average of three numbers. It's not associative. Try it.

So, to recap:

merge is associative and it has an identity ({}). It is a monoid! It is not commutative, however. Order of arguments does matter.

Conference alert ๐Ÿšจ

I booked my tickets to the 2019 Clojure/conj in Durham, North Carolina. If you're going, let me know! I haven't been to a Conj in a couple of years and I'm really excited.

Currently recording ๐ŸŽฅ

Property-Based Testing with test.check is now split into three courses, Beginning, Intermediate, and Advanced. The launch begins next week. Stay tuned for a flood of ideas, tips, and tricks around property-based testing.

Book update ๐Ÿ“–

I have been getting such great stories from readers of Grokking Simplicity. People tell me they are making their code more organized and testable. After all of the work I put into the book, it feels good that it's having such a great impact.

You can buy the book and use the coupon code TSSIMPLICITY for 50% off.

Thanks for reading.

Clojure Challenge ๐Ÿค”

This week's challenge

This week's challenge is a treasure hunt. Find monoids in the Clojure ecosystem. You can find them in Clojure's core libraries or elsewhere in contributed libraries. If you find 10, consider yourself a novice monoid hunter. 20 and you're an expert.

For extra credit, determine if they implement the Clojure monoid pattern and whether they are also commutative.

As usual, please reply to this email to send me your submissions. I'll collect them up and share them in the next issue. If you don't want me to share your submission, let me know.

Rock on!
Eric Normand