In case it's useful to anyone, a here's a little utility macro to force expressions to run in order by turning them into nested case expressions. It's just syntax sugar. See thread https://clojurians.slack.com/archives/C7Q9GSHFV/p1741009371615649 for an example of where this kind of thing might be handy. I'm a newb at writing macros but this seems to work ๐ค
(defmacro do-in-order
"Transforms (do-in-order a b c ...) into nested case expressions like (case a (case b (case c ...)))."
[& exprs]
(if (empty? exprs)
nil
(reduce (fn [inner# expr#]
`(case ~expr# ~inner#))
(last exprs)
(reverse (butlast exprs)))))Very nice!
If I may suggest a minor diff for the docstring: (case a (case b c ...)) -> (case a (case b (case c ...)))
Good idea, thanks! Have updated. Also added #'s to make inner# and expr# gensyms.
That isn't necessary is it?
I think you're right it's not necessary as they're only used in the anonymous function. I'm still getting the hang of macro writing ๐
I have one of these too ๐
(defmacro sequentially
"Run each of `exprs` in sequence."
{:style/indent 0}
[& exprs]
;; Use `case` to enforce sequence. See for example
;;
(when exprs
(let [[e & es] exprs]
(if es
`(case ~e (sequentially ~@es))
e)))) I tried writing mine with recursion at first too I couldnโt get it to work. I will study your version to learn from it
TIL ~@ unquote splicing ๐
Thereโs also #?@ splicing reader conditional which I learned about recently :^)
https://clojure.org/guides/weird_characters#_splicing_reader_conditional
reduce can auto-consume the first arg (or throw), leaving you with
(reduce #(list 'case %2 %) (reverse '(a b c)))@xifi should we add this to electric-contrib? If users feel like they need to write this, at least we can control the implementation
All, I want to be clear that this may be an anti-pattern, per https://xyproblem.info/ in my opinion there is insufficient justification for this and this may not be the right pattern for whatever problem you may be solving
((fn [] ...)) is a much better idiom, because it constrains you to use actual imperative statements (no latency, no reactivity) -- this is 100% sane. Therefore, if your problem can be refactored into an IIFE, i recommend doing that.
@dustingetz Iโve used it for this kind of thing:
(sequentially
(check-and-maybe-throw-exception)
(do-what-i-really-want-to-do))
It feels quite natural to me and it stops the second part running when it will fail, but maybe itโs not good.yes, given the number of users rolling their own it makes sense to include it in contrib
there's nuance to the different patterns, e.g. the last one can be
((fn []
(check-and-maybe-throw-exception)
(do-what-i-really-want-to-do)))
IFF the 2 fns are clojure(script) fns.
If one wants to run a platform effect after an electric call an IIFE still does the trick
((fn [_] (platform-effect)) (Electric-call))
Only if one needs to sequence 2 electric calls one needs case
(case (Electric-call1) (Electric-call2))things get trickier if we start adding reactive arguments
https://www.surveymonkey.com/r/getyourdiscount, mention Electric if you want to see an Electric talk
Done!
Is this only for previous conj attendees?
Now if they listen Iโm going to have to figure out making it to the Conj this year. ๐
@danieleneal don't think so
Thanks!