This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-08-06
Channels
- # announcements (4)
- # beginners (132)
- # calva (37)
- # chlorine-clover (60)
- # cider (1)
- # clara (12)
- # clj-kondo (40)
- # cljs-dev (109)
- # clojure (76)
- # clojure-dev (19)
- # clojure-europe (8)
- # clojure-france (17)
- # clojure-nl (4)
- # clojure-sg (1)
- # clojure-spec (14)
- # clojure-uk (7)
- # clojurescript (98)
- # conjure (96)
- # cursive (15)
- # data-science (2)
- # datalog (11)
- # datomic (24)
- # emacs (17)
- # figwheel-main (3)
- # fulcro (45)
- # jobs-discuss (1)
- # kaocha (3)
- # malli (2)
- # nrepl (1)
- # off-topic (135)
- # portal (2)
- # re-frame (17)
- # reagent (11)
- # reitit (4)
- # sci (60)
- # shadow-cljs (75)
- # spacemacs (3)
- # sql (32)
- # tools-deps (79)
- # vim (88)
- # xtdb (4)
I’m facing a weird behavior with str
(cljs). I observe the following difference:
(str "n" " blah") -> "n blah"
(str "\n" " blah") -> "\nblah"
(in the second case the space are removed (??)
is that an expected behavior or a bug?
interesting. wonder what version is it running
mine is “1.10.773”
cljs.user=> *clojurescript-version*
"1.8.40"
so seems like a bug in a more recent version?
how are you starting your repl?
clj -A:cljs -m cljs.main -re node -r
ClojureScript 1.10.597
cljs.user=> (str "\n" " blah")
"\n blah"
clj -A:cljs -m cljs.main -r
ClojureScript 1.10.773
cljs.user=> (str "\n" " blah")
"\n blah"
cljs.user=>
I think it is seeing it as an escape character, \e
for eg. returns Unsupported escape character: \e.
exception
hmm, I think it has something to do with Cursive repl
when I run this in it - this happens, but for the same version of clojur in raw repl it doesn’t
What is the correct way to effectively do the following? (assoc old-map :a 10 :b 20 :c 30)
If I have two maps old-map
and new-map
for which new-map
has value {:a 10 :b 20 :c 30}
.. I.e., I want any new values in new-map
to be added to the result and for any colliding keys, the value from new-map
should have precedence. If I use concat
I get a sequence of pairs, not a map, according to https://clojuredocs.org/clojure.core/concat
Hi everyone! Quick deployment question for you. Currently, we are deploying our app by creating an uberjar, uploading it to the server and using something like systemd
to run it while nginx fronts it. I was wondering if someone could point me to a resource which explains how one might do this process automatically? An annoying problem is when we have a small (or big, both are equally annoying :P) change, we implement a fix and then we have to go through the process of compiling the uberjar and uploading and so on. Thanks for your time!
Do you mean a development tool like Jenkins? (Or GitHub Actions)? You would still need to write some scripts probay but there is no reason not to automate deployment
Yes, Jenkins looks like the right thing. I’m a complete novice with this (not even familiar with the terms).
If you are running on GitHub, I would check GitHub Actions first (just for the sake of avoiding consuming a new tool). There are some available options https://github.com/marketplace?type=actions&query=deploy for deployment directly to Azure or AWS (depending on where you host your server). Jenkins is also a very commonly used tool, but you will need to “learn” it first
I would also recommend adding your JAR in a Docker container instead of deploy it directly to your server
Ok amazing, thank you! I’ll check this out. We’re deploying to a digitalocean droplet.
A good term to research is "Continuous Integration" - it's a general category of tool that can create a new deployable artifact (and run tests. create downloadable artifacts etc. etc.) out of your repo commits. Eg. a github project can use circleci to create a new uberjar and deploy it to staging for every push to the prod branch, with a notification going to slack reporting on success or failure on completioin.
then, you can have a pushbutton deploy of the same artifact to prod with jenkins
I disagree about docker - if you don't have native dependencies, an uberjar is simpler and less overhead and more secure.
Great, thanks @U051SS2EU for pointing me in the right direction!
You don’t have to run your container as root. With JAR you also have access to a potentially richer set of features and vulnerabilities of the host (depends on your VM/host configuration). I see docker as additional layer that should improve security of the system. Disclaimer: I’m not a huge proponent of (mis-)using docker just genuinely interested in why you think it’s less secure
Oh, so that’s more about trusting 3rd party images/dependencies? I feel we sort of do that as well with maven dependencies (although their power is probably more limited)
Also by JVM limiting permissions you mean it doesn’t give you full C-like access to the host OS? (though native code can still do anything)
it's not just third party deps - it's the amount of damage a third party could do by replacing or adulterating an artifact
the jvm also allows blocking specific classes (including the ones used for native access)
but then again, realistically, with the sort of "devops" model everyone uses today, there are bigger and easier targets
Reading lots of code from codebases that are representative of their language’s community norms, best practices, and successes seems like a good way to work towards fluency in a language. (A colleague of mine has been doing this with Go lately, reading the Kubernetes source and suchlike.) What are some Clojure projects that would make for good reading material in this way?
That is a great question. I am also interested in that!
I wonder if this is a particularly hard question for languages intended to be used the way Clojure is. The language was designed to be great for “information systems programming”, and I’d assume that most of the successful examples of that sort of system are enterprise applications in private repositories. So while you could probably just read the source code for key libraries like core.async
and learn a lot about what well-written Clojure looks like, you wouldn’t necessarily learn what it looks like to use Clojure to build the kind of systems it’s claimed to excel at from doing that.
One thing I can recommend is to check out the conduit real world app: https://github.com/gothinkster/realworld
Here is also a great Conduit real world app that is not in the list above. This one is clojurescript + react native. :) https://github.com/flexsurfer/conduitrn
I've asked this question in the community a couple of times. One concrete suggestion I got was https://github.com/mtgred/netrunner. Here's another one I came across via a podcast I heard: https://github.com/lipas-liikuntapaikat/lipas. I confess I haven't really dived into either yet, so I don't know how representative they are of 'best practices', but they are both real production code. With Rust or Go you'd be spoilt for choice. I'm not sure why there seem to be few open source large real-world Clojure projects.
Seen mentioned elsewhere here on Clojurians today, the Defold game editor: https://github.com/defold/defold/tree/dev/editor/src/clj/editor. Not the most typical Clojure use (a JavaFX app), but might be of interest.
There is one more big project that I know of. It is a react native application with ios, android and desktop support. https://github.com/status-im/status-react
I shared a bunch of links here: https://www.reddit.com/r/Clojure/comments/dp1p2t/any_mature_open_source_system_to_learn_about/
And thanks for linking defold! I ran CodeScene analysis of the project just for fun and here are files/content statistics:
@U06BE1L6T - I remember downloading & having a play with the defold editor back when it was open sourced. I was considering clojure as my next language to learn & wanted to see what people were doing with it. Cool project & one of the best examples of a 'real' app using JavaFX I've come across.
what is an idiomatic way to trigger some method after another one ends successfully? I can have an atom and use add-watch to trigger the method if state changed, not sure if it's the right way. basically I want my web application to fire some method after a handler finishes it jobs correctly, I do not want to call the method from the handler itself,
want to separate handlers with other tasks, and learn some new tricks as well :)
You can use a middleware, also those are usually used to add additional functionality to your handlers and not to call a different method
to be clear, slurp and spit do not work on binary data
look at what the http://clojure.java.io namespace provides (eg. io/copy) - eg. you can use io/copy to write a byte[] onto an output-stream
it's not as simple for input though, since our byte-arrays are fixed length
this is the easiest way I know of to put a binary file into an array:
(require '[ :as io])
(import ( ByteArrayOutputStream))
(let [o (ByteArrayOutputStream.)]
(-> (str (System/getenv "HOME") "/blah.jpg")
(io/file)
(io/copy o))
(.toByteArray o))
io/copy promises to close the inputstream it makes from the file
if you are referring to slurp, this is not safe, slurp and spit are for strings not binary
Hey guys, I'd like to use clojure(/script) in order to create a static website for myself. I'd like a hot-reloading style workflow wherein I can build out the site's HTML & CSS using hiccup & garden in a manner similar to react's styled components. I then wish to be able to build and output the site into a public directory similar to how Gatsby and other static site generators work. I think existing clojure static site generators such as cryogen[0] and stasis[1] could be used to solve the second problem, but I've yet to find a nice CSS workflow. Are hiccup and garden sufficient for this? If not, would you be able to recommend any further libraries/approaches I could incorporate. Many thanks! [0] https://github.com/cryogen-project/cryogen [1] https://github.com/magnars/stasis
There are some wrappers around Garden if you're looking for something more similar to styled components: https://github.com/roosta/herb https://github.com/Jarzka/stylefy
eh, when using map
to print
something, it always return coll of nil
element, is there any method like foreach
in Scheme, which doesn't return any value??
look at the do*
group of fns. doseq
seems to fit your description well
here I just want to get a matched regex value in a string, but not a list, is there any function I can use?
@steiner3044 run!
is precisely like foreach if I recall my scheme
(run! println coll)
prints each item in coll on a line, returns nil
@steiner3044 for something like readdir, check out http://java.io.File and java.nio.file.Paths - this is best handled via direct interop https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Paths.html https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/File.html
What should I do If I get an Unhandled clojure.lang.Compiler$CompilerException
should I try to isolate it and file a bug report? Or should I just fix the error in my code and continue along?
you can use *e
to see the details
usually it's a problem in your code, but the *e
will have a lot of detail to determine the nature of the problem
I have the complete error message in the emacs window:
Show: Project-Only All
Hide: Clojure Java REPL Tooling Duplicates (41 frames hidden)
2. Unhandled clojure.lang.Compiler$CompilerException
Error compiling src/clojure_rte/dfa.clj at (238:21)
#:clojure.error{:phase :macro-syntax-check,
:line 238,
:column 21,
:source
"/Users/jnewton/Repos/clojure-rte/src/clojure_rte/dfa.clj",
:symbol for}
Compiler.java: 7009 clojure.lang.Compiler/macroexpand1
Compiler.java: 7092 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 3104 clojure.lang.Compiler$MapExpr/parse
Compiler.java: 6797 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 3888 clojure.lang.Compiler$InvokeExpr/parse
Compiler.java: 7108 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 6120 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 6436 clojure.lang.Compiler$LetExpr$Parser/parse
Compiler.java: 7106 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 7094 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 6120 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5467 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4029 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7104 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 7094 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 38 clojure.lang.Compiler/access$300
Compiler.java: 596 clojure.lang.Compiler$DefExpr$Parser/parse
Compiler.java: 7106 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 7180 clojure.lang.Compiler/eval
Compiler.java: 7131 clojure.lang.Compiler/eval
core.clj: 3214 clojure.core/eval
core.clj: 3210 clojure.core/eval
interruptible_eval.clj: 91 nrepl.middleware.interruptible-eval/evaluate/fn
main.clj: 414 clojure.main/repl/read-eval-print/fn
main.clj: 414 clojure.main/repl/read-eval-print
main.clj: 435 clojure.main/repl/fn
main.clj: 435 clojure.main/repl
main.clj: 345 clojure.main/repl
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 155 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 190 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 189 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 832 java.lang.Thread/run
1. Caused by java.lang.IllegalStateException
Can't pop empty vector
PersistentVector.java: 470 clojure.lang.PersistentVector/pop
PersistentVector.java: 22 clojure.lang.PersistentVector/pop
RT.java: 751 clojure.lang.RT/pop
core.clj: 1474 clojure.core/pop
core.clj: 4641 clojure.core/for/to-groups/fn
core.clj: 944 clojure.core/reduce1
core.clj: 4639 clojure.core/for/to-groups
core.clj: 4708 clojure.core/for
core.clj: 4624 clojure.core/for
AFn.java: 165 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
Var.java: 705 clojure.lang.Var/applyTo
Compiler.java: 6992 clojure.lang.Compiler/macroexpand1
Compiler.java: 7092 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 3104 clojure.lang.Compiler$MapExpr/parse
Compiler.java: 6797 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 3888 clojure.lang.Compiler$InvokeExpr/parse
Compiler.java: 7108 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 6120 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 6436 clojure.lang.Compiler$LetExpr$Parser/parse
Compiler.java: 7106 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 7094 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 6120 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5467 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4029 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7104 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 7094 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 38 clojure.lang.Compiler/access$300
Compiler.java: 596 clojure.lang.Compiler$DefExpr$Parser/parse
Compiler.java: 7106 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6789 clojure.lang.Compiler/analyze
Compiler.java: 6745 clojure.lang.Compiler/analyze
Compiler.java: 7180 clojure.lang.Compiler/eval
Compiler.java: 7131 clojure.lang.Compiler/eval
core.clj: 3214 clojure.core/eval
core.clj: 3210 clojure.core/eval
interruptible_eval.clj: 91 nrepl.middleware.interruptible-eval/evaluate/fn
main.clj: 414 clojure.main/repl/read-eval-print/fn
main.clj: 414 clojure.main/repl/read-eval-print
main.clj: 435 clojure.main/repl/fn
main.clj: 435 clojure.main/repl
main.clj: 345 clojure.main/repl
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 155 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 190 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 189 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 832 java.lang.Thread/run
a macro popped an empty vector during expansion - unless that macro is part of clojure.core it's not a clojure bug
When I print *e, I get this
clojure-rte.core> *e
#error {
:cause "Can't pop empty vector"
:via
[{:type clojure.lang.Compiler$CompilerException
:message "Syntax error macroexpanding for at (/Users/jnewton/Repos/clojure-rte/src/clojure_rte/dfa.clj:238:21)."
:data #:clojure.error{:phase :macro-syntax-check, :line 238, :column 21, :source "/Users/jnewton/Repos/clojure-rte/src/clojure_rte/dfa.clj", :symbol for}
:at [clojure.lang.Compiler macroexpand1 "Compiler.java" 7009]}
{:type java.lang.IllegalStateException
:message "Can't pop empty vector"
:at [clojure.lang.PersistentVector pop "PersistentVector.java" 470]}]
:trace
[[clojure.lang.PersistentVector pop "PersistentVector.java" 470]
[clojure.lang.PersistentVector pop "PersistentVector.java" 22]
[clojure.lang.RT pop "RT.java" 751]
[clojure.core$pop invokeStatic "core.clj" 1474]
[clojure.core$for$to_groups__6291$fn__6293 invoke "core.clj" 4641]
[clojure.core$reduce1 invokeStatic "core.clj" 944]
[clojure.core$for$to_groups__6291 invoke "core.clj" 4639]
[clojure.core$for invokeStatic "core.clj" 4708]
[clojure.core$for invoke "core.clj" 4624]
[clojure.lang.AFn applyToHelper "AFn.java" 165]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.lang.Compiler macroexpand1 "Compiler.java" 6992]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7092]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler analyze "Compiler.java" 6745]
[clojure.lang.Compiler$MapExpr parse "Compiler.java" 3104]
[clojure.lang.Compiler analyze "Compiler.java" 6797]
[clojure.lang.Compiler analyze "Compiler.java" 6745]
[clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3888]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7108]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler analyze "Compiler.java" 6745]
[clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6120]
[clojure.lang.Compiler$LetExpr$Parser parse "Compiler.java" 6436]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7106]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7094]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler analyze "Compiler.java" 6745]
[clojure.lang.Compiler$BodyExpr$Parser parse "Compiler.java" 6120]
[clojure.lang.Compiler$FnMethod parse "Compiler.java" 5467]
[clojure.lang.Compiler$FnExpr parse "Compiler.java" 4029]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7104]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7094]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler access$300 "Compiler.java" 38]
[clojure.lang.Compiler$DefExpr$Parser parse "Compiler.java" 596]
[clojure.lang.Compiler analyzeSeq "Compiler.java" 7106]
[clojure.lang.Compiler analyze "Compiler.java" 6789]
[clojure.lang.Compiler analyze "Compiler.java" 6745]
[clojure.lang.Compiler eval "Compiler.java" 7180]
[clojure.lang.Compiler eval "Compiler.java" 7131]
[clojure.core$eval invokeStatic "core.clj" 3214]
[clojure.core$eval invoke "core.clj" 3210]
[nrepl.middleware.interruptible_eval$evaluate$fn__2448 invoke "interruptible_eval.clj" 91]
[clojure.main$repl$read_eval_print__9068$fn__9071 invoke "main.clj" 414]
[clojure.main$repl$read_eval_print__9068 invoke "main.clj" 414]
[clojure.main$repl$fn__9077 invoke "main.clj" 435]
[clojure.main$repl invokeStatic "main.clj" 435]
[clojure.main$repl doInvoke "main.clj" 345]
[clojure.lang.RestFn invoke "RestFn.java" 1523]
[nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84]
[nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56]
[nrepl.middleware.interruptible_eval$interruptible_eval$fn__2474$fn__2478 invoke "interruptible_eval.clj" 155]
[clojure.lang.AFn run "AFn.java" 22]
[nrepl.middleware.session$session_exec$main_loop__2575$fn__2579 invoke "session.clj" 190]
[nrepl.middleware.session$session_exec$main_loop__2575 invoke "session.clj" 189]
[clojure.lang.AFn run "AFn.java" 22]
[java.lang.Thread run "Thread.java" 832]]}
clojure-rte.core>
I'm not using any of my macros in this code.
so the *e reveals it was inside for
to_groups__6291$fn__6293 invoke "core.clj" 4641]
you probably have a function called to-groups
notice how the contents of *e
are clearer and more detailed than what emacs provides
looks like it's trying to expand the macro for
right
Here's the function I'm trying to compile:
and as @dpsutton points out, it looks like to-groups has a bad for inside it
(defn complete
"Render complete the given Dfa.
If it is already complete, then simply return it,
otherwise compute a new Dfa which has been completed, but
adding a sink state if necessary and add at most one transition
per state to the sink state."
([dfa]
(let [incomplete (find-incomplete-states dfa)]
(if (empty? incomplete)
dfa
(complete dfa incomplete))))
([dfa incomplete]
(let [sink-state (or (first (find-sink-states dfa))
(let [sink-id (first (filter (fn [id]
(not ((:states dfa) id))) (range)))]
(map->State :index sink-id
:accepting false
:transitions (list [:sigma sink-id]))))]
(make-dfa dfa {:states
(for [:let [current-states (states-as-seq dfa)
extended-states (if (member sink-state current-states)
current-states
(conj current-states sink-state))]
q extended-states
]
[(:index q) (if (member q incomplete)
(let [existing-labels (map first (:transitions q))
new-label (if (empty? existing-labels)
:sigma
`(~'and :sigma (~'not (~'or ~@existing-labels))))]
(map->State
(assoc q
:transitions (conj (:transitions q)
[new-label (:index sink-state)])))))])}))))
yeah, :let
before any other clauses is the likely issue here- that' s super weird
how would for
decide how many times to run that let?
I'm not calling to-groups
directly anyway.
@jimka.issy I can replicate your error:
user=> (for [:let [x 0] y (range 10)] [x y])
Syntax error macroexpanding for at (REPL:1:1).
Can't pop empty vector
you need some binding before :let
Ah. so it is a bug in the for
macro. It should probably, check its assumption rather than emitting a compiler exception, I'd guess.
think about what for
tries to do here - as it does the recursive unwrap, it needs some input driving the :let
calls, there's no input to be found if let comes first
it's not a bug in for, it is a bug in your code
there are many places where clojure gives a proximate cause in an error message rather than a root cause, and the general policy is not to try to find root causes for errors and let programmers find them
Sorry, but It is a bug in for
, for is assuming the first entry is not :let
and not checking that assumption.
starting with :let
doesn't even have a coherent possible meaning
I think this is more a java question but I'm using bb, hoping to use it's transit support: https://github.com/borkdude/babashka/blob/ef06175678e421c99e870de9cb3b39a927ba7c9f/test-resources/babashka/transit.clj What would I need to do to read transit from standard in?
and that causes a compiler exception. Ok, its easy to fix once I see it. but at the very least I should get an error such as cannot-expand-macro...
yeah - I don't mean to be hostile here, I'm just trying to articulate my own explanation for these behaviors (there are many places the clojure compiler behaves this way)
in general, my observation is that if the clojure compiler has a choice between a higher level description of a cause, and a proximate immediate cause, you'll typically see the latter
though over time (especially with spec) we see more pre-checking
if for
ever has a spec in clojure.spec, it should definitely include in its spec that :let
can't be the first clause
oh, and btw. @dpsutton - to-groups
is a helper function inside the definition of for
itself
(defmacro for
"List comprehension. Takes a vector of one or more..."
{:added "1.0"}
[seq-exprs body-expr]
(assert-args
(vector? seq-exprs) "a vector for its binding"
(even? (count seq-exprs)) "an even number of forms in binding vector")
(let [to-groups (fn [seq-exprs]
(reduce1 (fn [groups [k v]]
(if (keyword? k)
(conj (pop groups) (conj (peek groups) [k v]))
(conj groups [k v])))
[] (partition 2 seq-exprs)))
there's assert-args
here already, I guess an assertion could be made that (first seq-args) isn't :let
I guess the first arg should neither be :let, nor :when, nor :while ???
same for doseq
?
i think i remember hearing about a ticket for this and rich declined to act on it saying just do it correctly (if i remember)
that sounds like how rich would respond to such a ticket