This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-01-18
Channels
- # announcements (2)
- # aws-lambda (14)
- # babashka (4)
- # beginners (66)
- # clojure (113)
- # clojure-spec (9)
- # clojure-uk (7)
- # clojurescript (24)
- # data-science (12)
- # datomic (45)
- # docker (2)
- # emacs (1)
- # fulcro (48)
- # jobs-discuss (22)
- # kaocha (7)
- # keechma (3)
- # luminus (7)
- # off-topic (7)
- # re-frame (6)
- # shadow-cljs (43)
- # spacemacs (18)
- # tools-deps (2)
- # vim (1)
- # vscode (2)
- # yada (11)
The source code is on github
@nxtk What sort of things are you looking for? Most of the books explain it at a high-level.
Each form is read and compiled to bytecode (functions become classes with static invoke
methods; namespaces become classes with static initializers). When a program starts up, main
is run (either clojure.main
or a designated, compiled "main" class -- namespace with a -main
function), and as each namespace is required, it is loaded (and compiled if not already) and then the namespace's (class's) static initializer is run. Then functions are invoked.
The process is essentially the same for the REPL, reading each form and compiling it and then executing the bytecode.
@seancorfield exactly that, but more and in-depth maybe there is blogs about compiler implementation and design choices or something like that
i just want to understand compiler more, reading java sources is not enough, because i don't see the full picture
I guess I'm not sure what "big picture" there is... Rich has talked about some of his choices and his overall goals in conference talks -- which are all online, many with transcripts.
The compiler is fairly simple really -- since Clojure as a language is pretty simple.
But maybe that's because I come from a compiler background... I co-wrote one of the first ANSI-validated C compilers and I wrote a C++ compiler front end. Clojure is a much simpler language (thank goodness!).
@seancorfield most likely i just need a crash course into compilers, any books you can recommend maybe?
The "dragon" book is the classic work on that, by Aho and Ullman, as I recall.
You might also find this interesting https://en.m.wikipedia.org/wiki/Lispkit_Lisp -- see also the FP book linked in the page by Henderson. It's a good read.
Those two works by Henderson were the inspiration for my PhD work back in the mid-'80s (the design and implementation of functional programming languages).
@nxtk when talking about implementing lisps, https://github.com/kanaka/mal is amazing too if you already havent checked it out 🙂
Also i found http://www.duelinmarkers.com/2016/01/24/the-life-of-a-clojure-expression.html to be really enlightening too
I'm involved with a project building an online coding platform for learning Clojure. One thing we need to do is represent their solution as an AST for it to be analyzed for possible automated suggestions. As we are just beginning the design, I'm looking for suggestions for "inspiration".
depends on whether the suggestion is stylistic ("prefer if-not seq
to if not-empty
") or if it goes beyond that, e.g. a suggestion related to getting an algorithm right
don't know for the latter; for the former you can use kondo or eastwood using eastwood repeatedly is not a particularly explored area, but one can certainly make it work
well I imagine to be really complete it would include all syntactical information, like formatting
Like meander? That would be fun, I've been looking for some excuse to try that
clj-kondo
led me to rewrite-clj
and rewrite-cljs
A question about reflection. From a comment on https://clojure.atlassian.net/browse/CLJ-2400 by Alex Miller:
My guess is that reflection is picking an unintended override, probably because it's choosing based on some arbitrary ordering that happens to be different than other jdks.
As with any reflective case, you can stop getting arbitrary results by providing type hints on a and b
[...]
I don't think there's really anything to do here, and I don't think it's actually specific to J9.
Does it mean that when I have to interop with Java, I have to avoid reflection as much as possible? Because otherwise, it seems that it can be an undefined behavior given that just switching a JDK leads to different results.I had it in my wishlist to run all my builds (for context: ~50 libs/projects/...) against a grid of JVMs and also clj compiler flags (e.g. direct linking, no read-eval, elide-meta...) i.e. if a given test suite only passes against a very basic combination (jdk 8, no flags) that would probably indicate a problem
in practice, this kind of ambiguity is rare in my experience. we do run the Clojure test suite in a matrix build against different jdk versions and vendors, and this is typically not a problem
J9 is particularly different than the openjdk-based family (which is what most people use)
Is it different in a way that could affect more Clojure programs than a switch to a different JDK with the OpenJDK family?
I'm guessing the answer is "yes". Right now, I'm trying to understand why running shadow-cljs (which doesn't really use CLJ magic AFAIK) on OpenJ9 intermittently leads to errors caused by improperly used KeywordLookupSite
or its anonymous inner class. Sometimes it's used as a number, sometimes as a seq, sometimes something else.
it's definitely more different than the OpenJDK variants
since they all share a code base
Use case: I often find I create an arity that I use for recursion, but that does not make sense at the outer calling level… I’ll try if that :arglists
thing works for me…
Yay, works! Like so:
(defn wheres-waldo-bonus
"Find the path to waldo"
{:arglists '([waldo vektor])
:test (fn []
(is (= [7 1 0]
(wheres-waldo-bonus :W
[[:A :B :C]
[:D :E :F]
[:G :H :I]
[:J :K :L]
[:M :N :O]
[:P :Q :R]
[:S :T :U]
[:V [:W] :X]
[:Y :and :Z]])))
(is (= nil (wheres-waldo-bonus :W
[[:A :B :C]])))
(is (= [1]
(wheres-waldo-bonus :W
[:V :W :X])))
(is (= [1 2 1]
(wheres-waldo-bonus [:i :am :waldo]
[[:a :b "ceee"]
[[:i :am :not :waldo] [:i :am :spartacus] [:hello [:i :am :waldo] [:who :are :you?]]]
["d" "e" 6]]))))}
([waldo vektor]
(wheres-waldo-bonus waldo vektor []))
([waldo vektor path]
(when (vector? vektor)
(let [i (.indexOf vektor waldo)]
(if (> i -1)
(conj path i)
(->> (map #(wheres-waldo-bonus waldo %1 (conj path %2)) vektor (range))
(remove nil?)
first))))))
(Which is a solution to a challenge in the latest Purely Fubctional new letter.)Any feedback on this is very welcome. The challenge was about finding the path to a value in an arbitrary nested vector such that the path could be used with get-in
.
Curious about that local bound step function, @nxtk. Mayvbe it is cleaner than dabbling with arglists.
hmm just an informal term, most often I see "step" used to mean functions from state->state
it also looks like the waldo
argument is just being passed around unchanged, so that could simply be closed over in a local helper function
(defn wheres-waldo-bonus
"Find the path to waldo"
[waldo vektor]
(let [helper
(fn helper [vektor path]
(when (vector? vektor)
(let [i (.indexOf vektor waldo)]
(if (> i -1)
(conj path i)
(->> (map #(helper %1 (conj path %2)) vektor (range))
(remove nil?)
first)))))]
(helper vektor [])))
This is using a private function so losing the closing of waldo:
(defn- wheres-waldo-helper
[waldo vektor path]
(when (vector? vektor)
(let [i (.indexOf vektor waldo)]
(if (> i -1)
(conj path i)
(->> (map #(wheres-waldo-helper waldo %1 (conj path %2)) vektor (range))
(remove nil?)
first)))))
(defn wheres-waldo-bonus
"Find the path to waldo"
[waldo vektor]
(wheres-waldo-helper waldo vektor []))
I feel like I should be able to separate out the step of adding one more waypoint to the path, but right now the how eludes me…Usually I see just two functions. One is a public one and the other is a private one. It could also be argued that it's a more obvious way to convey the intention, and a bit more DRY (after all, you have to repeat the arguments in your solution).
Yes, that's what I meant. :)
If you want to have two functions/signatures, you're bound to have some repetition. But with :arglists
, there's one more.
Gotcha, I’ll see what it’ll look like with doing the work in a private function instead. Feels like that will open up an extra level of unit testing…
Not really - you don't need to test the private function. It's just a matter of organizing the code - all variants (`:arglists`, a local binding, an extra function) should behave identically unless there's some issue with Clojure itself. (Or unless I don't know something - always a possibility).
I don’t think I have to test each provate function. It’s just that… It might be a mirage, but I “see” a step where each waypoint is added to the path, and that it could be tested separately from the outer “can I find waldo” task. But right now the outer function just dispatches the whole task to the private one, so there is no point in adding separate tests.
Anyone have an idea why the namespace i defined as :aot
would compile twice when running lein uberjar
?
in cases like these, first thing I do is question assumptions: how do you know it is compiling twice?
I guess it don't but it writes it twice to stdout like so:
Compiling my-ns.core
Compiling my-ns.core
that could be what?
that it is not compiling twice, but for some reason you see on your terminal that string twice
that is just going to change my question 🙂
My other projects doesn't... so what triggers it to write it twice?
also it takes equal amount of time between the print lines on stdout. So it seems to do an equal amount of work behind the scenes.
but, do you see anything between the two lines? what do you see after the second line? is there any other line that shows twice? is there a pattern?
nothing between the 2 lines no 😞
well, if it compiled twice you should see same thing between two lines as after the second line, no?
Compiling my-ns.core
Compiling my-ns.core
Created /home/je/Projects/my-project/target/my-project-1.0.0-SNAPSHOT.jar
Created /home/je/Projects/my-project/target/my-project-1.0.0-SNAPSHOT-standalone.jar
one jar is the standalone and the other one is just the slim one... but usually it looks like so:
Compiling my-ns.core
Created /home/je/Projects/my-project/target/my-project-1.0.0-SNAPSHOT.jar
Created /home/je/Projects/my-project/target/my-project-1.0.0-SNAPSHOT-standalone.jar
it shouldn't need to compile twice... that jar stuff is just packaging
yes two different jars
I did lein uberjar in my project and I got a "Compiling ns-name-here" for each clj file anywhere inside my src or its children dreictories
you probably have :aot :all
in your project.clj
you can choose which namespaces you want to ahead-of-time compile.
I did it with :aot [my-ns.core]
I created a new empty project myself... and I'm seeing weird behavior here as well... though totally different 😬
this is going to be a fun night... I'll see if I can reproduce in a more simple setting and get back
I just hoped that someone had experienced it before and knew what weirdness could trigger such
anyways thanks so far
yeah, I am trying that to. I googled it and found some github issue so I am trying based on that. If I reproduce, will let you know
and here is the github issue that pointed the way https://github.com/technomancy/leiningen/issues/2521
thanks a lot... it sounds like the bug I caught 😉
👍 yeah thank you, too. I am new to clojure and it's tools so I stick my nose in other people's issues to prompt me to learn. I hope I was not too annoying
nope not at all
@nxtk i was also interested in the clojure compiling topic recently and in addition to what @rahul080327 mentioned, suggest at least checking out (if you haven't already done so): * @gfredericks' "what are all these class files even about?" conj talk: https://www.youtube.com/watch?v=-Qm09YiUHTs wonderful slides (e.g. see at around 11:50 and other places for a nice diagram of some of the relevant functions and code paths), though i haven't found those online anywhere. * @guilespi's http://blog.guillermowinkler.com/blog/2014/04/13/decompiling-clojure-i/ (and 2 more parts) also covers some similar material. nice to be able to compare.
@sogaiu Ah, good memory! Yes, Gary's talk is good material! Now you've mentioned that, I seem to recall @ghadi also did a Conj talk about Clojure internals a while back...?
Hmm, I think I was remembering this one from Conj '14 https://www.youtube.com/watch?v=S1mzUi_zbEs
@seancorfield thanks for pointing out that talk. it seems the first 9 minutes or so has some coverage of what a typical clojure function turns into as jvm bytecode with some accompanying description. the rest of the talk (invokedynamic and truffle) looks interesting too -- should make a note to come back to it 🙂
Hi 👋:skin-tone-2: I’m trying to read a ns declaration including metadata like row/column info. is there any clojure.tools.* lib i can use?
@jeroen.dejong You could try https://github.com/clojure/tools.namespace and/or clojure.tools.reader for this I guess. I'm not sure about preservation of row/column. If that doesn't work: https://github.com/borkdude/edamame is something I wrote for interpreting clojure code.