Fork me on GitHub
#beginners
<
2019-10-23
>
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.

noisesmith00:10:17

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

noisesmith00:10:33

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

noisesmith00:10:11

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

noisesmith00:10:02

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.

seancorfield00:10:09

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

like

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

andy.fingerhut03:10:29

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.

andy.fingerhut04:10:52

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 ["-Dmy.custom.jvm.property=true"]

andy.fingerhut04:10:18

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
seancorfield04:10:37

@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
seancorfield04:10:32

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

😮 4
jherrlin15:10:48

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)
     (~@forms)
     (do
       (timbre/error ~code)
       (timbre/error (s/explain-str ~spec ~data)))))


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

jherrlin15:10:03

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))
     (do
       (timbre/error ~code)
       (timbre/error (s/explain-str ~spec ~data)))))

bfabry15:10:26

you want ~@

bfabry15:10:53

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

jherrlin15:10:01

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

bfabry15:10:19

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

bfabry15:10:41

dammit slack

bfabry15:10:06

just turn it into ~@forms

jherrlin15:10:08

Still dont get it right

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

jherrlin15:10:25

Have to read up more about how this actually works.

bfabry15:10:57

no as in that whole map step

bfabry15:10:07

just delete it and replace it with ~@forms

bfabry15:10:22

pprint and macroexpand-1 are really helpful tools here

bfabry15:10:25

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

jherrlin15:10:18

That got it running!

jherrlin15:10:49

Thanks for the patience and the help!

bfabry16:10:47

no worries

hiredman19:10:47

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

hiredman19:10:36

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

fappy20:10:51

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 …

fappy20:10:28

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

walterl20:10:19

@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?

andy.fingerhut21:10:04

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: https://github.com/Engelberg/better-cond

fappy21:10:54

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

walterl21:10:02

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

walterl21:10:50

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

fappy21:10:58

(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!")
from https://clojuredocs.org/clojure.core/condp#example-542692d2c026201cdc326f6c

👏 4
walterl21:10:48

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

(let*
  [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!")))))

seancorfield21:10:43

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