clojure 2026-02-11

So I ported pedestal from interceptors to ti-yong/transformers just for shits and giggles, to see what it looks like. It's definitely a different way of doing things: https://github.com/johnmn3/ti-yong/blob/763bfa9aa6b45a50e8c64c0e74d71e80211afde4/hearth/examples/bookshelf/src/bookshelf/handlers/users.clj Might make it an actual server lib - currently called "hearth" - but it's just an experiment right now. It certainly does violate a number of our sensibilities. Turning functions into data and then making new ones via data manipulation is a novel concept allowing for some interesting capabilities not usually available in our normal lego-block-closure model. Instead, you get invokable maps where the data inside creates the behavior of the function. So it solves some of the problems interceptors solved, with closure wrapping of middlewares. I'm not entirely sold on the idea yet. But it's super intriguing to me and I think it deserves a little debate. What say you? Clojure heresy or potential new way to do things?

One cool thing... Because ti-yong/transformers are so moldable, you can attach specs to maps and the specs follow the maps around: https://github.com/johnmn3/ti-yong/blob/763bfa9aa6b45a50e8c64c0e74d71e80211afde4/hearth/examples/bookshelf/src/bookshelf/specs.clj

Hmm... Maybe claude didn't hook the specs up to the context map yet... Here's an example of what it looks like to have a map throw a spec error when you try to dissoc a guarded key from it: https://github.com/johnmn3/ti-yong/blob/4a101a78b81133c474cf30df2dc18d77270c42d6/src/ti_yong/alpha/transformer.cljs#L202

I like it: :res instead of :response

Lol and a few other small tweaks

The :res is actually a ti-yong ism and I let it ride

Just checked, and I see that shared requires (like Elixir, C# and other langs) are actually possible in Clojure as is. (Screenshot/code in thread.)

oh this is delightfully wicked, thank you

๐Ÿ˜‡ 1

Skeptics of data-readers are now fully convinced

๐Ÿคฎ 2
๐Ÿคฃ 2
Alex Miller (Clojure team) 2026-02-11T02:33:35.563599Z

Omg, please don't do that

๐Ÿ˜‚ 2
โ˜๏ธ 4
๐Ÿ˜Ž 1

and maybe add support for some C-style #include guards, the real missing piece of clojure? I always wanted to do (#ifndef SHARED_REQUIRES) (#define SHARED_REQUIRES) .. ๐Ÿ˜‰

๐Ÿ”ฅ 1
๐Ÿ˜‚ 1

I have functions in dev only that require a set of namespaces, each with a standard alias, so I can pull them into whatever ns I happen to be working in. But using them in the actual :require block seems like a nightmare waiting to happen.

Wouldn't it be awesome if if-let and when-let accepted multiple bindings - only assigning the but-first bindings if the first binding was truthy? To avoid nesting of lets

(if-let [user (get-user)]
  (let [last-login (get-last-login user)
        ...]
    ...))
could be written as
(if-let [user (get-user)
         last-login (get-last-login user)]
 ...)

๐Ÿ‘ 1

Nah, I don't think it will happen either. And I guess that it would be confusing - better-cond's versions require all bindings to be truthy, whereas my suggestion is that only the first binding needs to be truthy. Although I think my version makes more sense, it would probably not be clear to others. I agree that defining more functions is probably the best way - but I sometimes find that it would be nice to keep everything together and not have to have all these nestings. For me, this pattern seems to occur mostly in code that is wrapped by when-let and then let where the innermost code does some mutation.

Oh, didn't realize you were only dispatching on the first one, that seems odd to me. More logical and consistent with the rest of the language would be to have the rest of them behind a :let [other-bindings] type syntax, I think. Like for, doseq , etc.

๐Ÿ‘ 1

I commonly use a macro called when-let*

(defmacro when-let*
  [bindings & body]
  (if (seq bindings)
    `(when-let [~(first bindings) ~(second bindings)]
       (when-let* ~(drop 2 bindings) ~@body))
    `(do ~@body)))

note that this isn't quite the same as what OP wants

OP probably would want something like

(defmacro when-let'
  [[l r & binds] & body]
  `(when-let [~l ~r]
     (let [~@binds] ~@body)))
 
(defmacro if-let'
  [[l r & binds] then else]
  `(if-let [~l ~r]
     (let [~@binds] ~then)
     ~else))
that being said, I feel like it's more confusing than anything (especially the supposed if-let' , which I don't even know if they want the bindings to only come into effect in the then branch).

overall though, the best way to avoid crazy nesting simply is to define more functions

๐Ÿ‘ 3

Btw, you're doing two queries to the database. It could have been just one:

(let [user-id (:user-id request)
      last-login (get-last-login user-id)]
 (if last-login ...))

(I too tend to split out nested if's as function calls, nice and compact, easy to reason about)

Or, if it's a map, I do use cond->

and most importantly you'll thank yourself when you read the code after a while

๐Ÿ‘ 2

@brjann I agree it should be in core, but I don't think it will happen. One thing you could use is this library, which has all those: https://github.com/Engelberg/better-cond