clojure

john 2026-02-11T13:59:20.857469Z

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?

john 2026-02-11T14:00:23.291839Z

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

john 2026-02-11T14:06:47.312029Z

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

john 2026-02-11T14:14:42.135599Z

This is what it looks like to use transformers in a frontend framework: https://github.com/johnmn3/comp.el/blob/2b880b8b897bc95a3bb2159c46f77c68f6832353/ex/src/todomvc/views/comps.cljs

2026-02-12T01:34:53.863369Z

I like it: :res instead of :response

john 2026-02-12T02:18:51.515859Z

Lol and a few other small tweaks

john 2026-02-12T02:19:40.294689Z

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

john 2026-02-12T02:19:58.839279Z

For "result"

isak 2026-02-11T02:28:04.758799Z

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

2026-02-11T12:08:34.048869Z

oh this is delightfully wicked, thank you

๐Ÿ˜‡ 1
2026-02-12T01:17:21.968249Z

Skeptics of data-readers are now fully convinced

isak 2026-02-11T02:28:15.396879Z

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

Omg, please don't do that

๐Ÿ˜‚ 2
โ˜๏ธ 4
๐Ÿ˜Ž 1
lassemaatta 2026-02-11T03:23:57.329589Z

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
Steven Lombardi 2026-02-11T04:25:14.561939Z

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.

DrLjรณtsson 2026-02-11T06:45:20.531819Z

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
DrLjรณtsson 2026-02-13T12:11:24.706419Z

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.

isak 2026-02-13T14:50:02.059989Z

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
dharrigan 2026-02-11T08:22:23.032409Z

I commonly use a macro called when-let*

dharrigan 2026-02-11T08:22:41.216809Z

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

dharrigan 2026-02-11T08:24:32.731199Z

exitsandman 2026-02-11T08:25:46.874649Z

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

exitsandman 2026-02-11T08:27:05.536419Z

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

exitsandman 2026-02-11T08:38:57.154489Z

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

๐Ÿ‘ 3
igrishaev 2026-02-11T08:39:08.020629Z

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

dharrigan 2026-02-11T08:41:15.737339Z

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

dharrigan 2026-02-11T08:41:51.928459Z

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

exitsandman 2026-02-11T08:45:30.694219Z

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

๐Ÿ‘ 2
isak 2026-02-11T16:18:02.118179Z

@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