Fork me on GitHub

I am trying to add values in a spec as below

(def input [::id ::name ::address])

(s/def ::test (s/keys :req input))

Getting - Syntax error macroexpanding s/keys at (macros.clj:112:15).
Don't know how to create ISeq from: clojure.lang.Symbol

Actual syntax is (s/def ::test (s/keys :req [::id ::name ::address)) - But how can I pass vector to req keys ?


you can't pass a value. you have to use a literal vector ther


same here. think of macros as syntax. there's no values involved

user=> (def x ['a 1])
user=> (let x (+ a 1))
Syntax error macroexpanding clojure.core/let at (REPL:1:1).
x - failed: vector? at: [:bindings] spec: :clojure.core.specs.alpha/bindings
the x there is a vector of [a 1]. But let doesn't want something that resolves to a vector, it needs a literal vector of two things: the binding symbol and the value


The syntax is that its a literal association of symbol to value


Sorry , what is the meaning of using literal vector?


@U11BV7MTK How can we sdo that ?>


the same as the let example above. You can't use a value input which is a def for a vector, you have to use the vector itself


(s/def ::test (s/keys :req [::id ::name ::address]))


hmmm.. but my requirement is req keys are cretaed dynamically and it will be stored in a collection! So any alternative ?


@U01J3DB39R6 What we do at work is treat the specs as the "system of record" and then use s/form to pull the specs apart and get the list of keys. Spec 2 will allow dynamic sets of keys because it supports programmatic specs. However, it's not being developed right now (other priorities and still some design issues need hammock time). So I get I'll ask, what problem are you trying to solve in the large? If you have specs, they are checked for all keys in a map, regardless of what's in :req...


But you can use regular Clojure to check all keys are present -- and know that all specs are checked for keys in a map.


@U04V70XH6, I am here trying to write a general spec, I am generating required keys dynamically! So for different input I get different type of req-keys , So I was planning to generate those keys and pass it to :req dynamically ,


Not possible with Spec 1.


Like I say, you can check "requiredness" with regular Clojure and you can check all qualified keys just by having the Specs for them.


If you had (s/def ::test (s/keys [])) and you validate {::id 42} against that, it will still check the value of ::id because you have a spec for it - even tho' it is not listed in ::test


(a lot of people don't realize that all keys get checked even when they're not listed in s/keys)


@U04V70XH6, That is strange right? If we pass some invalid keys and it is returning true values,


Not sure what you mean.


Spec is designed to validate by key.


user=> (s/def :acct/person (s/keys :req [:acct/first-name]
                            :opt [:acct/phone]))
Syntax error compiling at (REPL:1:1).
No such namespace: s
user=> (require '[clojure.spec.alpha :as s])
user=> (s/def :acct/person (s/keys :req [:acct/first-name]
                            :opt [:acct/phone]))
user=> (s/explain :acct/person {:acct/first-name  "Bugs"
                                          :acct/address  "address"})


it suppose to say :acct/address is not present right ?


Huh? Why would it say that?


Maps are open by design.


You only said :acct/first-name was required.


user=> (require '[clojure.spec.alpha :as s])
user=> (s/def ::id int?)
user=> (s/def ::name string?)
user=> (s/def ::test (s/keys :req []))
user=> (s/valid? ::test {::id "foo" ::name 42})
user=> (s/explain ::test {::id "foo" ::name 42})
"foo" - failed: int? in: [:user/id] at: [:user/id] spec: :user/id
42 - failed: string? in: [:user/name] at: [:user/name] spec: :user/name


☝️:skin-tone-2: It will validate all keys that have specs.


Requiredness is a separate concern. Spec 2 addresses this with schema and select.


I agree to your point, But if I pass just a invalid keys in a spec , I thought I would expect that , there is no such key defined in a spec


If there's a spec defined, the key will be checked. If there's no spec defined, it's "ok" by definition.


That's pretty much what "open by design" means. Not sure why you'd expect different?


Ahh I see, that's how the design is ! Thanks for your time and detailed explanation 🙂


Thanks to @U11BV7MTK also for your inputs


And for requiredness: (every? #(contains? data %) [::id ::name ::address]) will check that all expected keys are present -- so it can be dynamic.


(and, like I say, Spec 2 addresses this shortcoming of Spec 1)


is spec2 released ?


No, it's experimental (and, as I noted above, no longer being worked on due to other priorities).


There's a repo for it, but no releases.


At some point, Rich will figure out the remaining parts of the design, and it'll get "finished" and integrated into the core of Clojure.


For a while we had a branch of our code at work that was tracking the Spec 2 repo -- and it has some really neat stuff in it and some really interesting ideas -- but it never got to "production" quality and there are some big unanswered questions still to be resolved.

Ricardo Sawir06:10:55

Hi, want to ask if there's a recommended library to access and modify .env?


I have two functions that parse some params:

(defn parse-params [{:keys [response relay-state] :as params}]
  {:pre [response]}
  (cond-> params
          response (update :response (comp util/bytes->str encoding/unb64))
          relay-state (update :relay-state (comp util/bytes->data encoding/unb64))))

(defn- parse-params2 [{:keys [response relay-state] :as params}]
  {:pre [response]}
  (cond-> request-params
          response (update :response (comp encoding/unzip encoding/unb64))
          relay-state (update :relay-state (comp util/bytes->data encoding/unb64))))
They are almost the same, however, there is one slight change in how the response is decoded. Is there a clever way to write this instead? Seems to be a lot of redundant code. I also thought about parsing the function as well, but it seems to come out of nowhere.


make the functions that are different params to parse-param function


What @U0LAJQLQ1 said ☝️

(defn parse-params
   (parse-params params util/bytes->str)) ; <-- default
  ([{:keys [response relay-state] :as params} decode-fn]
   (cond-> params
     response (update :response (comp decode-fn encoding/unb64))
     relay-state (update :relay-state (comp util/bytes->data encoding/unb64)))))


sorry, i was really tired when i wrote that. depending on if you want to have the outside world change the behaviour of your function, you have some options, but they require more code than just having a function as a param. multimethods and protocols can give you polymophism too. if you get to the point where the function as a param becomes a problem you can look into those ways of solving your problem.


Perhaps this doesn't belong here, if so, mods please feel free to remove or point me to correct channel. I need some general advise or inputs. I'm exploring writing a English -> Sinhala transliterator using Clojure. Basically it would convert some sequence of English characters to corresponding Sinhala string. Some examples:

a -> අ
aa -> ආ
k -> ක්
ka -> ක
kaa -> කා
ki -> කි 
dhyaa -> ධ්‍යා
mama -> මම
madhyama -> මධ්‍යම

So there's both simple replacements and combinations. Perhaps there's ambiguity but I'm not sure. I'm thinking of using something like and writing out the rules as a parser grammar. Any thoughts on this plan? Is there other alternatives I can explore?


1. I think #C053AK3F9 is a great fit for this question. 2. I think instaparse should work. Not sure about the ambiguity, but if we're lucky, you can put the aa rule before the a rule and hope that leads to aa matching working as expected. 3. I don't know of any other alternatives. :)

👍 1

@UC1DTFY1G: have you explored, or more generally the use of for defining complex string-to-string relations? There’s a lot of interesting prior art there for the kind of linguistic string transformation you’d be doing here.

❤️ 1

@U02STT6CVK5 No, this is the first time I'm hearing about this. I will check it out. Thanks very much


I hope you find it useful! It’s very cool stuff, and I wish it were more widely known (and that there were good libraries for working with it in more languages). By the way, if you end up continuing to do language-processing work on Sinhala using functional programming, you might also be interested in Grammatical Framework:

❤️ 1

i've done some instaparse work. ambiguous grammars is a pretty big problem, and a real langauge (not a typical constructed language that gammars are mainly written for) will produce a lot of problems generating consistent parsing. if you just want a mapping, even a bit of a complicated mapping that involves a processing step to make the map, then instaparse is probably going to hurt much more than it helps. if you could show some examples of the non-trivial translations that would help a lot.


a map in clojure can represent very complicated translations compared to other languages. you can have arrays as keys, you can access nested maps using arrays.

{"a"{nil "tr1" "comb1" tr2}}
something like that could represent translations including combinations, and (get-in ) can work to translate. but, we really need examples to offer any guidance

👍 1

i just looked at the examples again, it wasn't obvious to me that "aa" is a combination, same with "kaa".


dhyaa -> ධ්‍යා
madhyama -> මධ්‍යම
making a grammar for handing this is going to be tricky. but, i think it's a pretty small grammar to handle the last 3 translations, (2-3 lines of grammar) just make sure you test with exhaustive parsing (instaparse has a function that gives you all the possible trees for running your grammar on a string). you want that list to be 1 item, otherwise translating the tree to sinhala is going to be a bit hard.

👍 1

(defn parses 
  "Use parser to parse the text.  Returns lazy seq of all parse trees
   that completely parse the text.  If no parse tree is possible, returns
   () with a Failure object attached as metadata.
you want to use that for testing


i have found to be very helpful in taking the nodes of a parse tree and translating them to something else. if the tree is very nested then clojure.walk works well with match to do translations, though debugging that stuff can sometimes be very tricky.


@U0LAJQLQ1 Thanks very much for the suggestions. I had written a trivial grammar for a subset of the language yesterday and I did run into the problem of incorrect parse trees. I used the parses function and I see that the correct parse is also in there though not as the first element. I will grapple with it a bit more and come back for any help.


you have to change the grammar to fix that type of bug. if (parses produces more than 1 tree than you will need to make a more complicated translation function (maybe that is the easier path to go down...?). i spent a lot of time fixing my grammar to not produce ambiguous parsing. it's difficult because very small changes in the grammar can have very large effects.


one issue i can see is in aa and kaa and dhyaa you will have to work a bit to make a grammar that respects the longer parses over the shorter ones. dhyaa is likely to be parsed as [:node "dhy"][:node "aa"]


@U0LAJQLQ1 On the other hand, multiple parses might be useful in a real transliteration context. They can provide the user with multiple matches for their input (like a drop down - This is what Google input tools does). We can apply some rules over the produced parse trees (For example in Sinhalese vowels can appear only at the beginning of a word) to produce multiple valid outputs? Just thinking out loud.


@U0LAJQLQ1 Yes. I’ve tried to put the most specific rules (dhyaa) at the bottom of the grammar and build up from there.


you likely don't want anything like your parse output to hit the user, and you probably want to have a good idea of what your parse output will be (reduce ambiguous parsing). you are going to translate your parse tree to something else that maybe your user could see. for my parsing i had a few translations before my final output, but i was doing programming language translations (doing paradigm translations too).


instaparse lets you order your parsing using a special character (i felt like it was like cheating a bit, but i had to use it for some of my issues)


i'm not sure order of your grammar matters at all. PEG ordered choice does that job


Great. I will check this out. I’m pretty sure there’s a long way to go to get stuff working properly.


Actually, I could get a decent parser working. A simple grammar:

S = word (whitespace word)*
word = (any | vowel | pureconsonant | consonant)+
vowel = e | E | a | U | O | i | u | A | I | o | aa | ii | ei | AA | ou | au | sru | srU
pureconsonant = T | d | n | K | w | s | f | L | p | j | G | J | v | B | P | t | k | b | r | y | g | l | N | h | m | D | dh | ch | Dh | xd | xj | Sh | xb | sh | xg | Ch | Th | th | thh | xmb | chh | xdh | xgdh | xkdh
consonant = pureconsonant vowel
any = #".*"
whitespace = #"(\s|\n|\t)+"
Looks ok so far. There are more rules to be added, but this itself covers about I guess 95+% of the cases. It is possible to write without the rest of the rules, but I will try put them in.


looking good

Ben Lieberman14:10:20

I am trying to figure out why I can evaluate this namespace twice and get this error the first time and then it works on the second try:

Evaluating file: core.clj
; WARNING: random-uuid already refers to: #'clojure.core/random-uuid in namespace: monger.util, being replaced by: #'monger.util/random-uuid
; Execution error (ClassNotFoundException) at (
; org.joda.time.DateTime
; Evaluation of file core.clj failed: class clojure.lang.Compiler$CompilerException
I have tick in my deps.edn, which is the only library I know of that depends on JodaTime, so why it wouldn't put it on my classpath I'm not sure. But I don't require Tick in the namespace in question, and I actually suspect that this error is originating with monger instead, because the first time I saw it was when I required the monger.json namespace in another file. What am I missing?


Can you share the contents of core.clj? In general the way to troubleshoot this is to comment out everything in the namespace, confirm it works and then start adding it back one by one (first the requires/imports, then the code) until you find out what's causing it.

Erick Bodine20:10:09

I keep getting the following ‘Unhandled REPL handler exception’ from cider-nrepl. I am on a Mac - my $JAVA_HOME & $PATH appear to set appropriately as well.

(base) ➜  cljgen echo $JAVA_HOME
(base) ➜  cljgen echo $PATH

nREPL server started on port 54957 on host localhost - 
nREPL 0.9.0
Clojure 1.11.1
OpenJDK 64-Bit Server VM
Interrupt: Control+C
Exit:      Control+D or (exit) or (quit)
user=> (System/getProperty "java.class.path")
ERROR: Unhandled REPL handler exception processing message {:code (System/getProperty "java.class.path"), :id 25bb31e2-c1db-443a-8af6-d68eb5999b8f, :op eval, :session 244132a2-0eb7-4547-bd08-9197ed1bd55c}
Syntax error macroexpanding at (track_state.clj:156:8).
	at clojure.lang.Compiler$InvokeExpr.eval(
	at clojure.lang.Compiler$InvokeExpr.eval(
	at clojure.lang.Compiler$DefExpr.eval(
	at clojure.lang.Compiler.eval(
	at clojure.lang.Compiler.load(
	at clojure.lang.RT.loadResourceScript(
	at clojure.lang.RT.loadResourceScript(
	at clojure.lang.RT.load(
	at clojure.lang.RT.load(
	at clojure.core$load$fn__6908.invoke(core.clj:6161)
	at clojure.core$load.invokeStatic(core.clj:6160)
	at clojure.core$load.doInvoke(core.clj:6144)
	at clojure.lang.RestFn.invoke(
	at clojure.core$load_one.invokeStatic(core.clj:5933)
	at clojure.core$load_one.invoke(core.clj:5928)
	at clojure.core$load_lib$fn__6850.invoke(core.clj:5975)
	at clojure.core$load_lib.invokeStatic(core.clj:5974)
	at clojure.core$load_lib.doInvoke(core.clj:5953)
	at clojure.lang.RestFn.applyTo(
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$load_libs.invokeStatic(core.clj:6016)
	at clojure.core$load_libs.doInvoke(core.clj:6000)
	at clojure.lang.RestFn.applyTo(
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$require.invokeStatic(core.clj:6038)
	at clojure.core$require.doInvoke(core.clj:6038)
	at clojure.lang.RestFn.invoke(
	at cider.nrepl$handler_future$fn__2278$fn__2279.invoke(nrepl.clj:53)
	at clojure.lang.AFn.applyToHelper(
	at clojure.lang.AFn.applyTo(
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1990)
	at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1990)
	at clojure.lang.RestFn.invoke(
	at cider.nrepl$handler_future$fn__2278.invoke(nrepl.clj:52)
	at clojure.lang.Delay.deref(
	at clojure.core$deref.invokeStatic(core.clj:2337)
	at clojure.core$deref.invoke(core.clj:2323)
	at cider.nrepl$wrap_tracker$fn__2483.invoke(nrepl.clj:482)
	at nrepl.middleware$wrap_conj_descriptor$fn__942.invoke(middleware.clj:16)
	at nrepl.middleware.session$session$fn__1381.invoke(session.clj:325)
	at nrepl.middleware$wrap_conj_descriptor$fn__942.invoke(middleware.clj:16)
	at nrepl.server$default_handler$fn__1949.invoke(server.clj:141)
	at nrepl.server$handle_STAR_.invokeStatic(server.clj:24)
	at nrepl.server$handle_STAR_.invoke(server.clj:21)
	at nrepl.server$handle$fn__1917.invoke(server.clj:41)
	at clojure.core$binding_conveyor_fn$fn__5823.invoke(core.clj:2047)
	at java.base/
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(
	at java.base/java.util.concurrent.ThreadPoolExecutor$
	at java.base/
Caused by: zip file is empty
	at java.base/$Source.zerror(
	at java.base/$Source.findEND(
	at java.base/$Source.initCEN(
	at java.base/$Source.<init>(
	at java.base/$Source.get(
	at java.base/$CleanableResource.<init>(
	at java.base/<init>(
	at java.base/<init>(
	at java.base/java.util.jar.JarFile.<init>(
	at java.base/java.util.jar.JarFile.<init>(
	at java.base/java.util.jar.JarFile.<init>(
	at cider.nrepl.middleware.track_state$fn__3437.invokeStatic(track_state.clj:155)
	at cider.nrepl.middleware.track_state$fn__3437.invoke(track_state.clj:155)
	at clojure.core$map$fn__5935.invoke(core.clj:2772)
	at clojure.lang.LazySeq.sval(
	at clojure.lang.LazySeq.seq(
	at clojure.lang.RT.seq(
	at clojure.core$seq__5467.invokeStatic(core.clj:139)
	at clojure.core$map$fn__5935.invoke(core.clj:2763)
	at clojure.lang.LazySeq.sval(
	at clojure.lang.LazySeq.seq(
	at clojure.lang.RT.boundedLength(
	at clojure.lang.RestFn.applyTo(
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$mapcat.invokeStatic(core.clj:2800)
	at clojure.core$mapcat.doInvoke(core.clj:2800)
	at clojure.lang.RestFn.applyTo(
	at clojure.lang.Compiler$InvokeExpr.eval(
	... 51 more


some tool, some nrepl middleware, you are using is throwing an exception as you code to evaluate is passing through all the layers of middleware before being evaluated

Alex Miller (Clojure team)21:10:59

the root exception is: "Caused by: zip file is empty" from cider.nrepl.middleware.track_state$fn__3437.invokeStatic(track_state.clj:155)

Alex Miller (Clojure team)21:10:39

not sure if whatever jar file that is should be not empty or that middleware should be better able to handle that, but that seems like the thing to look at

Erick Bodine21:10:47

@U0NCTKEV8 hmmmm, would that something be in the ~/.m2/repository/ directory or in the <project>/.cpcache?

Alex Miller (Clojure team)21:10:01

wouldn't be in .cpcache

Alex Miller (Clojure team)21:10:21

might be in m2 repo, but I don't know what that does


I would move to #C0617A8PQ, bring it up there, they might want to open a bug for it something. basically the classpath lein built for the java process it launched, that is running your clojure repl, includes an empty file


same exception as

user=> (java.util.jar.JarFile. "/dev/zero")
Execution error (ZipException) at$Source/zerror (
zip file is empty


and cider includes some nrepl middleware that attempts to open all the jar files and look for clojure source in there

Alex Miller (Clojure team)21:10:40

it is possible for maven downloads to fail, leaving bad stuff (but could also be an actually empty jar for some reason)

Erick Bodine21:10:20

i will try #C0617A8PQ , interestingly, a new directory containing an empty core.clj & a stripped down deps.edn works just fine.


well, lein and deps.edn are different, so that is different


depending on what kind of repl you are creating with deps.edn, nrepl wouldn't be involved at all, so a problem with nrepl middleware just won't exist

Erick Bodine21:10:53

failing project is deps.edn as well


oh, sorry, I just saw all the nrepl preamble print out and assumed lein


but a stripped down deps.edn makes sense to, because it is likely one of the deps in there that is the empty jar file


find ~/.m2/ -type f -empty might tell you which jar is empty, and if it isn't empty on purpose, if you delete it it might download again correctly

Erick Bodine21:10:06

good find!

(base) ➜  repository find . -type f -empty
(base) ➜  repository ls -l ./clj-commons-exec/clj-commons-exec/1.0.7/clj-commons-exec-1.0.7.jar
-rw-r--r-- 1 ebodine staff 0 Oct  6 10:53 ./clj-commons-exec/clj-commons-exec/1.0.7/clj-commons-exec-1.0.7.jar


@U0NCTKEV8 that is an awesome thought to find empty jars. I’ve had that a few times

Erick Bodine21:10:31

yeah, the download is bad. keeps pulling down an empty jar file

Erick Bodine21:10:45

that is definitely the problem - thanks again @U0NCTKEV8

Erick Bodine21:10:43

and @U064X3EF3 for bad maven download idea


i think its a bad artifact in clojars

👍 1
Alex Miller (Clojure team)21:10:02

yeah, I was wondering that too :)