hyperfiddle 2025-03-13

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 ๐Ÿ˜

๐Ÿ‘ 1

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

๐Ÿคฉ 2

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 ๐Ÿ˜

๐Ÿ†’ 1

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

Dustin Getz (Hyperfiddle) 2025-03-13T12:40:43.324319Z

@xifi should we add this to electric-contrib? If users feel like they need to write this, at least we can control the implementation

Dustin Getz (Hyperfiddle) 2025-03-13T12:44:14.489429Z

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

Dustin Getz (Hyperfiddle) 2025-03-13T12:44:56.992349Z

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

๐Ÿ‘€ 2

things get trickier if we start adding reactive arguments

Dustin Getz (Hyperfiddle) 2025-03-13T12:36:19.047629Z

https://www.surveymonkey.com/r/getyourdiscount, mention Electric if you want to see an Electric talk

๐Ÿ‘ 4
๐Ÿค˜ 4
๐Ÿค˜๐Ÿผ 1
โœ… 2

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. ๐Ÿ™‚

๐Ÿ’ฏ 1
๐Ÿ™‚ 1

@danieleneal don't think so

Thanks!

๐Ÿ’ฏ 1