You're right, I was being crude. I should have said: Having defined a spec, you also get more than something just capable of producing boolean-valued expressions, like ways to generate data of wanted shape and s/conform to "pick apart" and say in what way a value satisfies a spec.
:thumbsup:
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?
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
Adding a delay is just swap + n on the atom
If you read a string like that from a file or user input, there is no need to escape them.
If you have a string typed into your Clojure source code, then it is a lot like a Java or C string, where there are special characters like \n for a newline, so a backslash itself must also be escaped if you want a backslash in the resulting string.
I've never heard of anyone writing a bit of code that takes such strings you want to put in your source code, and escape them for you, but it wouldn't be difficult to write one if you really want it.
Most such strings in source code are relatively infrequent, and I believe developers typically escape them by hand-editing their programs.
(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 documentationFound the error, the value from the on-change event is a String and needs to be converted to int
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.
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)] ...)
Wow, cool. Thanks for sharing! I will try to refactor the code with this idea :thumbsup:
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.
Not yet, but support for that is imminent !
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?
Have you tried restarting your REPL?
Did that solve your issue?
Yes it did
Seems like subvec
error e.g. (subvec [] 1)
if (subvec [] "1")
then the error is class String cannot be cast to class Number
Awesome! Good luck with the rest of your project, just ask if there's anything more
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
Iโm confused why, because isnโt that what I did in the fdef?
you just supply the symbol
You need to pass s/exercise-fn a fully namespace qualified symbol. A handy way is to write `label-map
The backtick will resolve the symbol, for instance label-map => namespace.example/label-map`
The backtick worked perfectly, thank you
(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}])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.
What does this error message mean?
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
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?
apply
(apply sh args)
Right!
Thanks.
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)
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?
something like that
ah, i'm reading now i gotta quote them
or provide custom readers for them - thus the extra arg
What is it expecting, exactly? I think it's something like... a map from the quoted missing tag name to, anything?
I'm figuring I'd assign identity
until i get what I'm looking at
I'll keep looking for an example in or near the docs.
oh, i might get what i need from datascript docs. that's the name in this unknown tag
Still having trouble but I took it to #datascript
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.
I'm not sure what you can pass on opts to reader, likely how to interpret any new data/literals.
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!
Repl stands for Read Eval Print Loop
When you print the lazy sequence, it attempts to realize every element.
Your input is read, the code is evaluated, then the result is printed
okay, that makes sense now. Thank you very much!
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?
@jakub.stastny.pt_serv You can only use unquote-splice in a list (or something which expands into a list):
(defmacro foo [xs] `[~@xs])
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))))
(so that is what "list" refers to)
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=>
As your macro stands it would print BEFORE/AFTER at macro-expansion time -- I assumed you wanted that printed at runtime instead?
@seancorfield yes at runtime.
@seancorfield great, this seems to solve it.
@seancorfield @borkdude thank you guys ๐:skin-tone-3:
I have a lot to study, macros are really new to me. Very cool thing though!
The first rule of Macro Club is...
...don't use macros ๐
(but, yes, sometimes they delayed evaluation is exactly what you want)
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)))
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.
@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!
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 ...
.
(apply sh "zsh" "-c" chunks)
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.
Ah, I see. Cool the (doc ..)
thing, I didn't know about it at all.
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)))))))))
(although the source isn't always very instructive, esp. when it just calls into Java under the hood!)
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.
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.
I've seen introductions on using clojure.core.repl
with socket servers. They all pass Java properties. What might the .NET equivalent be?
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).
Thanks! I'll look there.