A bit of a shower-thought - but I realized recently that I don’t really get the design behind a lot of the core language macros. They all feel kinda… Lisp-y and not Clojure-y and I’ve sorta been scratching my head about it.
Just as an example, looking at let
and ns
Now I’m mostly just talking about the syntactic sugar here - since it’s a macro there can be all sorts of dark magic under the hood. But the syntax sorta doesn’t help one visually parse what’s going on and see what the expected input is
Starting with let
:
You have something like
(let [var1 stuff
var2 morestuff
var3 evenmore]
(somefunc
var1
var2))
why are the bindings being done in what looks like a vector? It seems a bit misleading syntax-wise.
I see a vector and my mental image is “okay this is going to be a list of stuff”.
Sometimes it’s something a bit trickier where the index/position has some implied meaning (like in hiccup). I think that’s a bit icky b/c there is an implicit contract you need to know a priori. It’d prefer a map in that case - like you see in cljfx
- but it’s a bit more verbose I guess. Here the hidden contract is that the vector needs to have an even number of terms…
But we have stuff that comes in pairs - I’d totally expecting a map
(let {var1 stuff
var2 morestuff
var3 evenmore}
(somefunc
var1
var2))
My lizard brain goes:
curly brace → pairs of stuff
The only issue is you’d need to drop Clojure’s let
being a Scheme/Lisp let*
b/c the order currently matters … but I don’t think that’s a biggie (it’d probably be a performance win)
The ns
macro goes a step further:
(ns mynamespace
(:require
[something :as thing1]
[anotherthing]
[lastthing])
(:import
CrazyJava.Stuff
CrazyJava.Thing))
Here we have a multiarity signature where it turns out the order actually doesn’t matter (except for the first element). And then each term is a weird lists of stuff (not in a vector) but the list has a special reserved slot for a keyword in the first position. It almost seems to suggest there is some logic to having multiple lists with the same key ex: (ns myns (:require ...stuff...) (:require ...things...)
. (is there…?)
my lizard brain is completely flumoxed. I’m a bit embarrassed to say, but for the longest time I’d just not even bother trying to remember the quirky ns
syntax and I’d copy/paste a ns
block from another .clj
file and then tweak it to do what I want.
There is a lot of implicit data-layout contract going on here that you just need to memorize and follow
Here I’d really expect something that looks a bit more like the deps.edn
(ns mynamespace
{:require [[something :as thing1]
[anotherthing]
[lastthing] ]
:import [CrazyJava.Stuff
CrazyJava.Thing]})
(this can be improved on)
To me it feels more consistent this way and you immediately know what structure to expect. You also know repeating keys isn’t meaningful and the order is meaningless
I feel I’m probably the one in the wrong here and I missing some logic behind it all so before I embarrass myself too much - can someone help me figure out what I’m missing?