Fork me on GitHub
Zac Bir00:10:11

Are nested lets idiomatic? Seems that it’s the right thing to do to keep bindings at their appropriate scope, but I haven’t seen a lot of it in example code I’ve stumbled across.


nested lets are unidiomatic, each binding can refer to all preceding bindings


instead of nesting bindings when you have side effects, use the idioimatic no-op binding _

Alex Miller (Clojure team)00:10:52

or don't use side effects :)


yeah, I was figuring referring to prior bindings and side-effects were the two reasons someone would nest a let inside a let, and covering each case


that said, (let [x (foo) _ (bar)] x) is a peeve of mine - just move (bar) out of th binding block into the body :/

Zac Bir00:10:10

Okay, that makes sense.


Also, if you think you still need nested lets, maybe consider breaking your function into smaller pieces.

Ian Fernandez03:10:29

hey people, I'm using deps.edn clojure and I want to get the alias variable on repl

Ian Fernandez03:10:23


clj -A:test
I want to know what variable contains the :test keyword


I believe that the '-A:test' argument is not passed to the JVM process. It is 'consumed' by the clj or clojure command before the JVM process is started.


You could define the alias :test inside of your deps.edn file to pass additional arguments to the Java process command line, using a key/val pair like :jvm-opts [""]


There is also the :main-opts key inside of deps.edn that an alias can have, causing additional command line arguments to be added to the Java command line.

clj 4

@d.ian.b If it helps, you can think of clj running in two phases: the first phase figures out the desired classpath (download libraries as needed), the list of desired JVM options, and the list of desired Clojure "main" options -- and those are all cached (into .cpcache) -- and the second phase takes all that cached data and runs java ... with appropriate options. So, by the time Clojure is running, nothing about the original clj invocation is available.

clj 4

(if you run the same command a subsequent time, it can just reuse the cached data -- so startup is much faster that time)

😮 4

Hey! I am trying to write a macro but i am failing. The macro below works fine. But I would like to use it without the do form.

(defmacro validate
  "Validate data against a spec, if OK execute forms. Else log code and spec error message.
  `code`   - Unique random string, used to locate the execution place in src code.
  `spec`   - Clojure spec.
  `data`   - Data to validate against the spec.
  `forms`  - Forms to evaluate if spec validation is OK."
  [code spec data forms]
  `(if (s/valid? ~spec ~data)
       (timbre/error ~code)
       (timbre/error (s/explain-str ~spec ~data)))))

(validate "pe8DJKuyoIzFPUaC5mWHIdq" ::company/company company
                (println "gabba gabba hey")
                (+ 1 1)))


Trying things like this but i get unbound fn failures.

(defmacro validate
  [code spec data & forms]
  `(if (s/valid? ~spec ~data)
     (do ~(map (fn [form] ~form) ~forms))
       (timbre/error ~code)
       (timbre/error (s/explain-str ~spec ~data)))))


you want ~@


so that the list that is the result of the map function gets spliced in, rather than just dumping a list


Dont really get it, trying this but still failing ~@(map (fn [form] ~@form) ~@forms)


the first @ means everything in the following list is unquoted, so you shouldn’t be unquoting ( and ~ @) anything inside that list


dammit slack


just turn it into ~@forms


Still dont get it right

(defmacro validate
  [code spec data & forms]
  `(if (s/valid? ~spec ~data)
     (do (map (fn [form] form) ~@forms))
       (timbre/error ~code)
       (timbre/error (s/explain-str ~spec ~data)))))


Have to read up more about how this actually works.


no as in that whole map step


just delete it and replace it with ~@forms


pprint and macroexpand-1 are really helpful tools here


user=> (defmacro foo [& forms]
  #_=>   `(do ~forms))
user=> (clojure.pprint/pprint
  #_=>   (macroexpand-1 '(foo 1 2 3)))
(do (1 2 3))
user=> (defmacro foo' [& forms]
  #_=>   `(do ~@forms))
user=> (clojure.pprint/pprint
  #_=> (macroexpand-1 '(foo' 1 2 3))
  #_=> )
(do 1 2 3)


That got it running!


Thanks for the patience and the help!


no worries


the above macros are broken in that they may evaluate spec and data multiple times, writing it as not a macro would be simpler and avoid that kind of thing


(fn [code spec data f] (if (s/valid? spec data) (f) (do-whatever code spec data)))


hi 🙂 Is there something like cond-> that short circuits? I have a value and want to execute the right expression depending on which 1-arity predicate returns true for it, out of a hard-coded set of such predicates …


… and it seems a little verbose to use cond when the value is going to be fed into each of these predicates in turn


@fappy Something like

(foo-> x
   pred1 do-thing1
   pred2 do-thing2
   pred2 do-thing2
   ;; ...
which expands to this:
(if (pred1 x)
  (do-thing1 x)
  (if (pred2 x)
    (do-thing2 x)
    (if (pred3 x)
      (do-thing3 x)
      ;; ...
Is that what you mean?


I'd recommend looking through the options available in this library, although I do not know off the top of my head whether any of them match what you are asking for:


I get the sense that there is a good reason why cond-> does not short circuit, but I don’t know what it is


I'd guess it's because that's the idea behind threading 😜

Lennart Buit21:10:22

yeah, it applies threadings if the test preceding it is truthy


I.e. cond is probably closer to the behavior you're looking for, than cond->


(condp #(%1 %2) :foo
  string? "it's a string"
  keyword? "it's a keyword"
  symbol? "it's a symbol"
  fn? "it's a function"
  "something else!")

👏 4

Just to "prove" the answer, that macro-expands to this:

  [pred__44187 (fn* [p1__44183# p2__44184#] (p1__44183# p2__44184#))
   expr__44188 :foo]
  (if (pred__44187 string? expr__44188)
    "it's a string"
    (if (pred__44187 keyword? expr__44188)
      "it's a keyword"
      (if (pred__44187 symbol? expr__44188)
        "it's a symbol"
        (if (pred__44187 fn? expr__44188)
          "it's a function"
          "something else!")))))


(actually, our condq would work here but it wouldn't be any better than condp: (condq #(%1 :foo) string? "it's a string" ,,,) 🙂 )