Fork me on GitHub
#beginners
<
2021-06-27
>
km02:06:12

Hey guys, I'm trying to design a running delay. Multiple processes could add time to the delay, and a function would run whenever the remaining time reaches zero. Any ideas how to simply implement this?

hiredman02:06:34

The easiest is just keep an atom, and have a loop that reads the atom sets it to 0, and sleeps until the atom is 0

🙌 3
hiredman02:06:57

Adding a delay is just swap + n on the atom

olaf09:06:57

(def pika-evolutions    (r/atom ["Pichu"
                                 "Pikachu"
                                 "Raichu"]))
(def selected-evl (r/atom nil))

(defn vec-remove
  "Remove elem in coll"
  [pos coll]
  (into (subvec coll 0 pos) (subvec coll (inc pos)))) ;; <--- Uncaught Error: Index out of bounds

(defn remove-evolution-form []
  [:div
   (into [:select {:size 4
                   :on-change #(reset! selected-evl (-> % .-target .-value))}]
         (for [idx (range (count @pika-evolutions))]
           (let [evl (get @pika-evolutions idx)]
             [:option {:key evl
                       :value idx} evl])))
   [:button
    {:on-click (fn []
                 (let [evl-left (vec-remove @selected-evl
                                            @pika-evolutions)]
                   (reset! pika-evolutions evl-left)))}
    "Remove Evolution"]])
1. I get an error when trying to remove an item from the ratom vector. Uncaught Error: Index out of bounds Where I'm doing wrong? 2. Do you have any hints on how to debug cljs properly? It seems that the js console doesn't help that much and I feel lost every time a new error came out. Atm I'm trying to reproduce the error with a small sample like the code above, but when the error persist I don't know where to debug properly or check for documentation

Nazral10:06:21

Keep in mind that I am way more familiar with Clojure than with CLJS, but when I have to debug things related to states in CLJS, I create a bunch of helper functions to allow me to dump it (or part of it) whenever I need. https://github.com/Tyruiop/syncretism/blob/main/datops-frontend/src/main/live_opts/core.cljs#L26. In some other cases I also create writer functions to allow me to interact easily with it.

Nazral10:06:18

Also I think you could so with only one atom (and have pika-evolutions be immutable), by doing something like (let [local-pika-evolutions (->> pika-evolutions (split-with #(not= % @selected-evl)) last)] ...)

olaf11:06:09

Wow, cool. Thanks for sharing! I will try to refactor the code with this idea :thumbsup:

Apple13:06:01

Seems like subvec error e.g. (subvec [] 1)

Apple13:06:54

if (subvec [] "1") then the error is class String cannot be cast to class Number

olaf10:06:12

Found the error, the value from the on-change event is a String and needs to be converted to int

aratare11:06:05

Hi there. I’m looking into moving from lein to clj deps for some of my projects, but they all contain some Java files. In lein this is easily set up with :java-source-paths. Does clj deps offer a way to do the same thing, i.e. to include Java source files along with the CLJ ones as well? My google-fu is not strong enough because I can’t seem to find anything about this. Thanks in advance.

Alex Miller (Clojure team)12:06:56

Not yet, but support for that is imminent !

Rob Haisfield12:06:28

I tried this code: (ns emotion-modeling-lfb.core (:require [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as stest] [clojure.spec.gen.alpha :as sgen])) (s/def ::between-0-100 (s/and int? #(> 101 % 0))) (s/exercise ::between-0-100) And got this error message on the exercise function: Execution error (FileNotFoundException) at emotion-modeling-lfb.core/eval1617 (form-init13010486330272312439.clj:1). Could not locate clojure/test/check/generators__init.class, clojure/test/check/generators.clj or clojure/test/check/generators.cljc on classpath. And [org.clojure/test.check “1.1.0”] is in my Leiningen project dependencies How can I resolve this?

Fredrik13:06:15

Have you tried restarting your REPL?

Rob Haisfield13:06:19

Just tried restarting the REPL, thanks for the tip. I was just reloading the file into the REPL, which I now know isn’t enough.

Fredrik13:06:10

Did that solve your issue?

Fredrik13:06:05

Awesome! Good luck with the rest of your project, just ask if there's anything more

Rob Haisfield13:06:25

Spec question:

(defn label-map [arousal valence]
  {:arousal arousal :valence valence})
(s/fdef label-map
        :args (s/cat :is-arousal ::between-0-100 :is-valence ::between-negative-100-and-100))
(stest/instrument 'emotion-modeling-lfb.core/label-map)
(s/exercise-fn label-map)
I receive the error code on exercise-fn: No :args spec found, can't generate

Rob Haisfield13:06:42

I’m confused why, because isn’t that what I did in the fdef?

dgb2314:06:16

you just supply the symbol

Fredrik14:06:25

You need to pass s/exercise-fn a fully namespace qualified symbol. A handy way is to write `label-map

Fredrik14:06:10

The backtick will resolve the symbol, for instance label-map => namespace.example/label-map`

Rob Haisfield16:06:19

The backtick worked perfectly, thank you

Rob Haisfield16:06:02

(defn emotion-map [arousal valence]
  {:arousal arousal :valence valence})
(s/fdef emotion-map
        :args (s/cat :is-arousal ::between-0-100 :is-valence ::between-negative-100-and-100))
(stest/instrument 'emotion-modeling-lfb.core/label-map)
(s/exercise-fn `emotion-map)
Okay, so now this is working fine when I only exercise the function ten times. Example output: `=> ([(26 -1) {:arousal 26, :valence -1}] [(15 0) {:arousal 15, :valence 0}] [(5 -1) {:arousal 5, :valence -1}] [(10 -2) {:arousal 10, :valence -2}] [(1 -1) {:arousal 1, :valence -1}] [(6 -1) {:arousal 6, :valence -1}] [(4 -1) {:arousal 4, :valence -1}] [(5 0) {:arousal 5, :valence 0}] [(7 -37) {:arousal 7, :valence -37}] [(1 29) {:arousal 1, :valence 29}])

Rob Haisfield16:06:42

Then I try

(s/exercise-fn `emotion-map 100)
and it works sometimes, but other times I get: Error printing return value (ExceptionInfo) at clojure.test.check.generators/fn (generators.cljc:435). Couldn't satisfy such-that predicate after 100 tries.

Rob Haisfield16:06:02

What does this error message mean?

dpsutton16:06:19

check out https://clojure.org/guides/spec#_custom_generators. The paragraph before this section hits this very error. Probably quite worth your time to read this guide carefully

Jakub Šťastný19:06:26

What's the CJ alternative to ...var in JS (or *var in Ruby) for list expansion for a fn that takes multiple arguments rather than one argument which is a list? I want to call something like (sh "ls" "foo"), but I have all the arguments in a list, so I need something like (sh ...args). How can I expend the list?

Jakub Šťastný22:06:17

What if I need to add something to the list, such as (defn zsh [& chunks] (sh "zsh" "-c" ...chunks))? Do I need to manually create a new list with all the arguments or is there a shortcut? In Ruby it'd be sh *["zsh", "-c", *chunks], just like in JS, only replacing * with ....

seancorfield22:06:48

(apply sh "zsh" "-c" chunks)

seancorfield22:06:24

dev=> (doc apply)
-------------------------
clojure.core/apply
([f args] [f x args] [f x y args] [f x y z args] [f a b c d & args])
  Applies fn f to the argument list formed by prepending intervening arguments to args.

Jakub Šťastný22:06:01

Ah, I see. Cool the (doc ..) thing, I didn't know about it at all.

seancorfield22:06:27

Also

dev=> (source apply)
(defn apply
  "Applies fn f to the argument list formed by prepending intervening arguments to args."
  {:added "1.0"
   :static true}
  ([^clojure.lang.IFn f args]
     (. f (applyTo (seq args))))
  ([^clojure.lang.IFn f x args]
     (. f (applyTo (list* x args))))
  ([^clojure.lang.IFn f x y args]
     (. f (applyTo (list* x y args))))
  ([^clojure.lang.IFn f x y z args]
     (. f (applyTo (list* x y z args))))
  ([^clojure.lang.IFn f a b c d & args]
     (. f (applyTo (cons a (cons b (cons c (cons d (spread args)))))))))

seancorfield22:06:13

(although the source isn't always very instructive, esp. when it just calls into Java under the hood!)

seancorfield19:06:45

(apply sh args)

mathpunk19:06:04

I would like to read some edn I didn't produce. Evidently there's a reader function I need to provide to (edn/read-string {:readers ...} s)

mathpunk19:06:58

What is it expecting, exactly? I think it's something like... a map from the quoted missing tag name to, anything?

mathpunk19:06:35

I'm figuring I'd assign identity until i get what I'm looking at

mathpunk19:06:27

I'll keep looking for an example in or near the docs.

mathpunk19:06:51

oh, i might get what i need from datascript docs. that's the name in this unknown tag

mathpunk19:06:26

Still having trouble but I took it to #datascript

Drew Verlee20:06:17

You don't have to pass read two args, if you just pass it a valid Edn as string I believe it should just parse it as clojure.

Drew Verlee20:06:07

I'm not sure what you can pass on opts to reader, likely how to interpret any new data/literals.

mathpunk20:06:48

had to put this down but, the string was getting parsed as clojure yet it had complaints about not being able to find... uh.... datascript.db in data-readers?

mathpunk20:06:52

something like that

mathpunk20:06:00

ah, i'm reading now i gotta quote them

noisesmith16:06:53

or provide custom readers for them - thus the extra arg

yiorgos21:06:16

When typing (map inc (range)) in the repl the form is executed and I have to ctrl-c to stop it. The doc for map says that it returns a lazy seq. If map returns a lazy seq, why it runs in the repl? Thanks!

hiredman21:06:04

Repl stands for Read Eval Print Loop

Joshua Suskalo21:06:20

When you print the lazy sequence, it attempts to realize every element.

hiredman21:06:44

Your input is read, the code is evaluated, then the result is printed

yiorgos21:06:15

okay, that makes sense now. Thank you very much!

Jakub Šťastný21:06:22

I have an issue with a macro. I simplified the macro to: (defmacro block [name & sexps] (println "BEFORE") ~@sexps (println "AFTER"))` And the usage to: (block "test" (println "A") (println "B")). But I'm getting unquote-splice not in list. What am I doing wrong? ---- I'd like to also check whether I'm doing the right thing. So the use-case is I'm using babashka to run some sysadmin stuff. block is supposed to print "Starting <block-name>", execute all the commands in the block (install this and that, compile emacs 28, that sort of stuff) and then print "Block <block-name> took <number> seconds." I went for a macro here, since a fn would evaluate all the arguments first, rather than sequentially, so when it'd print out "Starting <block-name>", at that point it would already have run all the sexps as well. I imagine macro is the answer to this issue here, is it not?

borkdude21:06:23

@jakub.stastny.pt_serv You can only use unquote-splice in a list (or something which expands into a list):

(defmacro foo [xs] `[~@xs])

borkdude21:06:35

the expansion of the backtick happens in the reader:

$ bb -e "'\`[~@xs]"
(clojure.core/vec (clojure.core/sequence (clojure.core/seq (clojure.core/concat xs))))

borkdude21:06:47

(so that is what "list" refers to)

seancorfield21:06:50

dev=> (defmacro block [name & sexps] `(do (println "Starting" ~name) (do ~@sexps) (println "Completed" ~name)))
#'dev/block
dev=> (block "foo" (println "hello") (println "world") (* 1 2 3))
Starting foo
hello
world
Completed foo
nil
dev=> 

seancorfield21:06:24

As your macro stands it would print BEFORE/AFTER at macro-expansion time -- I assumed you wanted that printed at runtime instead?

Jakub Šťastný21:06:29

@seancorfield great, this seems to solve it.

Jakub Šťastný21:06:52

@seancorfield @borkdude thank you guys 🙏:skin-tone-3:

Jakub Šťastný21:06:52

I have a lot to study, macros are really new to me. Very cool thing though!

seancorfield21:06:04

The first rule of Macro Club is...

seancorfield21:06:08

...don't use macros 🙂

seancorfield21:06:23

(but, yes, sometimes they delayed evaluation is exactly what you want)

seancorfield21:06:17

Bear in mind you could use functions, if you don't mind wrapping the commands in a fn:

dev=> (defn block [name thunk] (println "Starting" name) (thunk) (println "Completed" name))
#'dev/block
dev=> (block "foo" (fn [] (println "hello") (println "world") (* 1 2 3)))

seancorfield21:06:51

That transformation -- from & body to (fn [] ~@body) -- is fairly common in macros that wrap functions. See https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc.clj#L350-L362 for example.

Jakub Šťastný21:06:57

@seancorfield yeah I thought of that, but the intended result is essentially a (moreless) declarative DSL for specifying how the system should be (with (package <xyz>) rather than (install <xyz>)) and I didn't want to litter it with lambdas, as it really is meant to be a declarative abstraction. It's a good point though!

icosahedron23:06:06

Looking for advice on using ClojureCLR in an IDE? Calva seems like a good choice, given the VS Code <-> CLR connection? Looked at Cursive, and no obvious way to select the CLR.

icosahedron23:06:07

I have installed ClojureCLR with dotnet tools install --global Clojure.Main and can get a repl with just Clojure.Main. My understanding is that nrepl does not work with ClojureCLR.

icosahedron23:06:46

I've seen introductions on using clojure.core.repl with socket servers. They all pass Java properties. What might the .NET equivalent be?

seancorfield23:06:32

I don't know much about ClojureCLR but I'll point you at https://gitter.im/clojure-clr/community which is where the Clojure CLR community is, apparently (there's a #clr channel here but it just points to that Gitter URL).

icosahedron23:06:03

Thanks! I'll look there.

pez06:06:36

Calva only supports nREPL. I think there is an nREPL implementation for the CLR, but I haven’t tried it with Calva.