Hi all, anyone else here that’s a fan of Rich Hick...
# thinking-together
h
Hi all, anyone else here that’s a fan of Rich Hickeys thinking on language design? Please comment with your main take-aways! I’ve found his talks a treasure trove and his approach to go back to deep philosophy and fuse it with hard-earned dev experience a great inspiration! Here’s a link to a bunch of transcripts for those in the enviable position to not yet having devoured all of them ❤️ https://github.com/matthiasn/talk-transcripts/tree/master/Hickey_Rich Disclaimer: Obviously no one claims Hickey, including himself, invented all of the stuff he talks about, but his way to concretize, make use of, make understandable the stuff is just something appreciate deeply and learned a lot from.
So here are some of my favorites that Rich Hickey describes in his talks and that relates to Language Design thinking, right off the top of my mind: + Orthogonality as the fundamental principle for composable language features + Data as a universal interface + Respect and understand the nature of Value semantics + The map data structure as a flexible composition of orthogonal concerns (my formulation, derived from his considerations for spec) + Build on abstractions, not conretions + etc etc
j
So as a full time clojure programmer I definitely have a lot of opinions on this. I largely agree with the philosophy, but at the same time find some unease with it. I guess I want to have this conversation, but I also don't want to be super negative. Clojure is a fantastic set of tradeoffs that makes for a super productive programming environment. But I do worry that there is a lot of talk from rich that this model of programming is the only good one. That is something I think needs more push back in the clojure world. I think languages have something to learn from clojure. But clojure could learn a lot from other languages as well.
👍 2
c
@Jimmy Miller please share some of the tradeoffs! Such things are always under discussed
👍 1
j
So let’s take what I think is one of the most central features of Rich’s view, complecting. There is something absolutely right about about avoid complecting things. At the same time, what doesn’t get talked about much is that what you consider complecting or not is actually relative to your own concerns, to the concepts in your language, etc. I’ll give one simple of example of this. In C/C++ style languages, there is an important distinction between allocation memory, and initializing that memory. There are many programmers who dislike the complecting of these two things. They believe that these are separate concerns and should be handled separately. This of course does not make Rich’s list of things that complect. And for good reason. On the jvm, with its memory model, this is less of a concern, and really something you don’t have direct control over. But yet, complecting is 1) supposed to be an objective measure and 2) something to avoid. So, is allocation vs initialization complecting? Is it just the good kind of complecting? How do we decide? I’d say that complecting is much more relative than Rich suggests. To further look at trade offs being made, consider immutable datastructures. Immutability is fantastic. It really helps you reason about code. But a full fledge immutable only style of programming depends on the underlying memory system in order to be efficient. When I was first learning rust, I tried to write rust like I did my clojure. And I found that my rust was barely if at all faster than the clojure code I wrote. Why? Because in tight loops I was allocating and cleaning up intermediate data structures. Due to generational garbage collection on the jvm, this pattern is cheap. But not so in rust. Instead what I found is that mutability becomes less difficult to reason about in rust and waaaaay more performant due to its borrow checker. My basic point is that we need pay attention to our goals and our context. There are things that Rich criticizes, that with the right goals and context are incredibly valuable. And on the same hand there are things that Rich embraces that with the wrong goals and context can be incredibly detrimental. So given Clojure’s goals and context I think it succeeds quite a bit. There have been some failures along the way (spec). But overall it is a great language to learn from and a very productive language for certain classes of applications.
👆 1
💡 1
👍 1
h
@Jimmy Miller Thanks for the writeup! Your mentioning of
spec
as a failure piqued my interest. I’m not inside Clojure, just read about
spec
and found a lot of beautiful ideas went into it, so how has it failed in your perspective? (I found no easily googleable online content on the topic)
j
There are definitely a lot of good ideas in spec. But the implementation has caused a lot of issues. Its status as alpha means that there have been a number of breaking changes. But yet, Clojure itself depends on spec! (even though it is alpha) So it ships with clojure. Meaning you as a library author you don’t have a stable interface to work with. It has been 5 years and the alpha status hasn’t changed and in fact, it looks like the future will be spec.alpha2. Still unclear what that means. Spec by its very nature wants to allow you to have incomplete specs. But if a library decides to do this and you in your application want to check your specs, you will now get errors. That’s fine if you just want to enabling instrumentation for a certain namespace, but s/check-asserts is global. So now if someone has a broken assert in a library, there is nothing you can do. Kind of ruining this whole gradualness spec pitched. Requiring namespace qualified keywords for everything made it really hard to model real world systems. I worked on a medical system that went all in on spec. Trying to contort the data we had coming in to fit into the way spec wanted to think about things was a nightmare. This problem is even leading to new language features in clojure, to me a sign that it might not be the right approach. The error messages produced by spec are simply unreadable.
Copy code
(defn get-x [{:x x}] x)
=> Error ;; Shortened from actual output
Syntax error macroexpanding clojure.core/defn at (REPL:1:1).
{:x x} - failed: vector? at: [:fn-tail :arity-n :bodies :params] spec: :clojure.core.specs.alpha/param-list
({:x x}) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
[{:path [:fn-tail :arity-1 :params],
 :reason "Extra input",
 :pred (clojure.spec.alpha/cat
        :params
        (clojure.spec.alpha/* :clojure.core.specs.alpha/binding-form)
        :var-params
        (clojure.spec.alpha/?
         (clojure.spec.alpha/cat
          :ampersand
          #{(quote &)}
          :var-form
          :clojure.core.specs.alpha/binding-form))),
 :val ({:x x}),
 :via [:clojure.core.specs.alpha/defn-args
       :clojure.core.specs.alpha/params+body
       :clojure.core.specs.alpha/param-list
       :clojure.core.specs.alpha/param-list],
 :in [1 0]}
{:path [:fn-tail :arity-n :bodies :params],
 :pred clojure.core/vector?,
 :val {:x x},
 :via [:clojure.core.specs.alpha/defn-args
       :clojure.core.specs.alpha/params+body
       :clojure.core.specs.alpha/params+body
       :clojure.core.specs.alpha/params+body
       :clojure.core.specs.alpha/param-list
       :clojure.core.specs.alpha/param-list],
 :in [1 0]}]
There are a number of other issues. But overall I think spec really missed the mark. I don’t know of anyone who really is a big advocate of it. I really was excited when it came out. Really wanted to love it. But now avoid it.
❤️ 2
@hvrosen
🙏 1
c
Thank you for sharing