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
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
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
For "result"
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
Skeptics of data-readers are now fully convinced
Omg, please don't do that
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) .. ๐
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)]
...)
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.
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
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
@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