Fork me on GitHub
#clojure
<
2021-06-01
>
Nom Nom Mousse08:06:10

One returns a list, the other a string. I want both to return a string. How do I fix this?

user=> (apply merge-with (fn [& args] (str/join " " (flatten args))) '({0 ["abc"]} {0 ["def"]}))
{0 "abc def"}
user=> (apply merge-with (fn [& args] (str/join " " (flatten args))) '({0 ["abc"]}))
{0 ["abc"]}

Nom Nom Mousse08:06:43

(into {}
        (for [[k vs]
              (apply merge-with concat <list>)]
           [k (str/join " " vs)]))

thheller08:06:25

I'd probably make the merge or even group-by and then as a separate step join the strings (eg. clojure.walk or just reduce)

Nom Nom Mousse09:06:58

walk I hadn't heard of. Seems cool, but feels kinda un-clojuric: "do this then that" in one function.

noisesmith18:06:55

walk isn't "do this then that", it's "apply this transformation to each level of a nested structure", which is very "clojuric"

Nom Nom Mousse14:06:40

But you have one transformation you do at each level and then you have one you do at the end on the result, right?

(walk #(* 2 %) #(apply + %) [1 2 3 4 5])
This seems less complected to me:
(apply + (walk #(* 2 %) [1 2 3 4 5])

noisesmith14:06:59

walk itself is rarely needed - it's a building block for implementing post-walk and pre-walk, and more elegant than the two being mostly copy/paste of one another

👍 3
Nom Nom Mousse10:06:41

I'm trying the Clojure tools.cli https://github.com/clojure/tools.cli#quick-start in the REPL and am getting these errors:

my.program> (parse-opts {:port 80} cli-options)
Execution error (ClassCastException) at clojure.tools.cli/tokenize-args (cli.cljc:34).
class clojure.lang.MapEntry cannot be cast to class java.lang.CharSequence (clojure.lang.MapEntry is in unnamed module of loader 'app'; java.lang.CharSequence is in module java.base of loader 'bootstrap')
my.program=> (parse-opts "{:port 80}" cli-options)
Execution error (ClassCastException) at clojure.tools.cli/tokenize-args (cli.cljc:34).
class java.lang.Character cannot be cast to class java.lang.CharSequence (java.lang.Character and java.lang.CharSequence are in module java.base of loader 'bootstrap')
What is the correct usage from the REPL?

borkdude10:06:10

@endrebak

(parse-opts ["-p" "2020"] [["-p" "--port PORT" "Port number"]])

borkdude10:06:07

$ bb -e '(clojure.tools.cli/parse-opts ["-p" "2020"] [["-p" "--port PORT" "Port number"]])'
{:options {:port "2020"}, :arguments [], :summary "  -p, --port PORT  Port number", :errors nil}

Nom Nom Mousse10:06:00

To use it my way I'll do:

(defn -main [& args]
  (let [opts (if (map? (first args))
               (first args)
               (parse-opts args cli-options))]
    (start-app opts)))

borkdude10:06:11

It's probably better to write another function, since by convention -main functions receive string args

Nom Nom Mousse10:06:04

True! With a name like start-from-repl or start-with-map or something.

roklenarcic11:06:21

I have a callback function and it might use some of the dynamic bindings, which are not the same when the callback is called as when it was created. I was trying to solve it by using clojure.core/binding-conveyor-fn but that thing is private. What’s the best solution for this (capturing bindings when making a function)?

delaguardo12:06:35

you could simply use let

(def ^:dynamic *foo* 1)

(def ^:dynamic *bar* 2)

(def f
  (let [foo *foo*
        bar *bar*]
    (fn []
      (+ foo bar))))

(binding [*foo* 2 *bar* 5]
  (f)) ;; => 3 not 7

roklenarcic12:06:16

thank you so much flowthing

Nom Nom Mousse12:06:56

Are there strings containing command line commands that might break because I have to split them?

(clojure.java.shell/sh "echo hi") ;; fails
(clojure.java.shell/sh (clojure.string/split "echo 'hi there'" #" ")) ;; works in this case, but will it do so for all?

borkdude12:06:25

@endrebak babashka.process has a function called tokenize will will split shell strings for you

borkdude12:06:44

Since you also asked in #babashka I gave more info there

Nom Nom Mousse12:06:25

Thanks! I meant to delete some of the qs that weren't specific to bb, but I must have forgotten. Thanks again!

Nom Nom Mousse13:06:58

What am I doing wrong here?

user> (require '[clojure.java.shell :refer [sh]])
user> (sh "bash" "-c" "sleep" "1" ";" "echo" "hi")
;; => {:exit 1, :out "", :err "usage: sleep seconds\n"}

delaguardo13:06:00

bash -c expect a string this should work (sh "bash" "-c" "sleep 1")

Linus Ericsson13:06:11

yes (sh "bash" "-c" "sleep 1; echo hi")

Nom Nom Mousse13:06:20

Yes! That actually made it a lot easier too 🙂 Thanks

Nom Nom Mousse13:06:39

How can I dispatch a shell/sh job and then be notified when it is finished?

Nom Nom Mousse13:06:43

Jeez, that was simple:

(future (computation) (callback))

❤️ 3
clojure 3
noisesmith18:06:05

also, ProcessBuilder / Process are not very hard to use, and support a lot of thinks shell/sh doesn't do

noisesmith18:06:40

for example this runs lua and sends to its stdin after it starts:

let [pb (ProcessBuilder. ["lua"])
      p (.start pb)
      out (.getOutputStream p)
      in (.getInputStream p)]
  (spit out "print \"hello from lua\"\n")
  (slurp in))
returns the string "hello from lua\n" if you have lua installed

❤️ 2
Nom Nom Mousse14:06:34

Thanks! I'll remember that if I ever need more advanced functionality 🙂

Rina21:06:44

Hi, I have a vector of keys and a value. How can I create a map where the vector turned into nested keys and the value is the value of those keys? For example: This is the vector

[:a :b :c]
And this is the value:
{:name "John"}
I want the result to be:
{:a {:b {:c {:name "John"}}}}
Thanks

borkdude21:06:45

@rinam (assoc-in {} the-vector the-map)

dpsutton23:06:57

is there some magic config to help CircliCI understand test.check failures a bit better?

(defspec oops
  (prop/for-all [x gen/int]
    (even? x)))
And the output is

dpsutton23:06:52

I can get the output out of the "STEPS" tab, but would be nice if it was present in the "TESTS" report tab