This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-04-24
Channels
- # architecture (7)
- # beginners (73)
- # boot (4)
- # cider (48)
- # cljsjs (7)
- # cljsrn (27)
- # clojure (206)
- # clojure-boston (2)
- # clojure-italy (21)
- # clojure-nl (8)
- # clojure-spec (7)
- # clojure-uk (94)
- # clojurescript (126)
- # clojutre (7)
- # core-async (3)
- # cursive (7)
- # data-science (1)
- # datascript (4)
- # datomic (6)
- # duct (1)
- # emacs (19)
- # figwheel (1)
- # fulcro (31)
- # graphql (13)
- # jobs (5)
- # jobs-discuss (42)
- # keechma (4)
- # leiningen (10)
- # luminus (3)
- # mount (2)
- # nyc (3)
- # off-topic (37)
- # om-next (3)
- # onyx (45)
- # pedestal (2)
- # re-frame (4)
- # reagent (2)
- # reitit (16)
- # shadow-cljs (118)
- # spacemacs (10)
- # tools-deps (8)
- # vim (20)
Can anyone speak to the differences between no.disassemble
and @bronsa's tools.decompiler
?
no.disassemble
is a disassembler, it takes a class file and outputs symbolic bytecode, it doesn't do anything besides reading the binary class file and parsing it into clojure values - - tools. decompiler
is a decompiler : it includes a disassembler but also parses that symbolic bytecode back into a tools.analyzer
-like AST and it tries converting that back to pre-macroexpanded code
I keep getting errors when I include datomic-pro in the dependecies (with brand new templates such as figwheel and chestnut). does anybody have any idea?
I should probably speak to my use case as well because decompiling bytecode may not even be viable for it (no matter how much I love the perversity of using it in bizarre macro experiments). I'd just like to capture the environment inside a top-level binding, with environment corresponding to the env
variable injected into functions in most lexically scoped lisp interpreters: just collecting all the local bindings within a particular top level one or alternatively a mapping of params to their occurrences in the body (again, collapsed for everything inside a top-level var). It seems this may be possible with just tools.analyzer
, but I'm unclear how from just playing with it and reading the AST cheatsheet.
Thanks for answering all my questions! Although I should have watched your talk on tools.decompiler (which was great btw) first and then could have answered the part about that myself π

Since they meet in a similar AST form I should try and just work with that unless decompilation is absolutely necessary. I'm not sure what you mean about &env
, though. Are you saying if I call jvm/analyze
on one I'll be able to see it? The :env
key in ASTs isn't quite what I'm looking for.
user=> (defmacro foo [] (list 'quote (keys &env)))
#'user/foo
user=> (foo)
nil
user=> (let [a 1] (foo))
(a)
in forms macros expanded through t.a.jvm, &env
is bound to the :env
value of the analyzer at the point of macroexpansion, in forms macroexpanded through clojure it's a map of local symbol -> clojure.lang.Compiler$LocalBinding
nodes for that local
no, let/cc
captures the current continuation, &env
is just a mapping of the compiler lexical state
I think that's what I meant by "a local version." &env
is always delimited by the scope of the macro whereas let/cc
is global.
I think my confusion is how :env
differs from recursively applying mapcat
to all values in :params
below the top-level and scraping the bodies for occurrences of them (since they can occur in nested scope). It seems like there would be an AST key that specifically tags bound variables, though. Am I just missing it?
I'm not interested in this as a calling convention, but from the perspective of capturing the structure of nested locally bound variables. This actually relates exactly to our discussion about nested #(..)
the other day.
I'm assuming now I'm discounting let
s but otherwise would like to just have a map that captures the structure of where function arguments (both in a top-level defn
and any functions inside it) are bound to where they're used.
I'm wondering whether there are AST keys for where parameters are used that I'm just not noticing or whether I'd have to filter the bodies of all children for them. Or alternately, whether I could obtain information like this by actually running a function. I think it's my line of thought surrounding this last possibility that's confusing everything.
I still don't understand what you are asking for, sorry, if you give me an example of an input and what the output you want should be maybe I can help better
To clarify first, any values keyed with :op :local
should correspond to essentially arguments in things that desugar to fns
(I'm actually unsure whether that's the case for let
in Clojure, but would assume those are included as locals as well)?
any special form that introduces a binding, that binding is keyed by :op :binding
, any time a reference to that binding is made, that is keyed by :op :local
the :env
val holds all the local variables that are available at this point + metadata about the node
Oh, okay. So my confusion was looking at :env
in reverse order. I should have been going from the inner most scope out.
(wrt to the above example, I'm not fixed on maintaining the structure of locals as I demonstrated there. it was just a convenient example, consisting only of locals)
This is a case where, since the above wasn't immediately obvious to me in the AST structure, I explored different options to try and get at that information and they were so perverse I wanted to continue down that road. So I found a well-documented CPS library, although the implementation is rather naive and it also doesn't support delimiting them other than at the boundary of all functions you don't CPS, then got into running no.disassemble
on the continuations themselves...which obviously were so long as to overflow an emacs buffer so then I started parsing the strings to examine the stacks. I'm not even sure what the locals in the all the different stacks correspond to, though, since they didn't seem to be affected by what functions I was CPSing.
"disassembling" a continuation won't do what you think, what you were doing was disassembling the code that compiled to the function the continuation is an instance of
let me know if there's some better documentation that would've helped you figure this out earlier
One part was what you just said about "disassembling the code that compiled to the function the continuation is an instance of." I ended up with things like this: https://gist.github.com/Sophia-Gold/c11b4b942f6981849c57fbb635696339
The downside of not being forced to take that ludicrous approach is I wanted to title a blog post on this "Decompiling with Continuations"
Although, I may try out both approaches to my initial problem: one top down where I recursively filter for :op :binding
and :op :local
at each level and get back a nice nested map, and one bottom up where I just have the :env
values from the leaves.
>I ended up with things like this yeah so in clojure every function is compiled to a class and an instance of that class is made for the function
if you're decompiling the class you're not seeing any property about the runtime object of that function
Yup, I realized it was a Clojure vs. Scheme issue in my expectations and had a hunch about what you just told me.
nah it's not a clojure vs scheme, it's that that you were asking a completely different question to clojure
if you disassemble a function in scheme you'll get the same result, altho in some different representation, it will be the bytecode/machine code for that function
In the latter approach I mentioned above, I'd have to reconstruct the binding sites from the :env :locals {}
, right? This segues into answering your actual question about documentation: there's a ton on types of nodes, but only short verbal descriptions of the keys themselves so I have to infer a lot from the soft docs, i.e. "example usage."
ok, I'll try to write a bit more of a tutorial-like introduction when I have some time
Got it. I was unclear whether that information would be preserved in the keys to the hash-map somehow.
I did give a talk a few years ago about some of the main ideas in tools.analyzer if you're interested
and tim baldridge gave one the year before that is an interactive kind of walk through on how to implement transformation passes with t.a
Yes, I should probably dig into that. Tbh, I think I was turned off at first by it essentially breaking homoiconicity. That's really a property of Clojure, though.
Right. Basically what you said in your talk on tools.decompiler about needing to work at the AST level to implement any interesting transformations. That's true in other lisps as well.
I did watch Tim's talk quite a while ago and I think I conflated a lot of his use case with simpler ways I could use those ASTs. A lot of it is probably writing functions to reduce the verbosity to just what I care about for different purposes.
Anyway, so just two more things. First, regarding my adventures in CPSing Clojure functions: I'm still unclear what you meant by, "if you're decompiling the class you're not seeing any property about the runtime object of that function."
behind the scenes of (let [a 1] (fn [] a))
you can imagine it being transformed into something like
(deftype my_fn_123 [a] clojure.lang.IFn (invoke [] a))
(let [a 1] (my_fn_123. a))
and you're decompiling the my_fn_123
type/class, not anything about any of its instances, like (my_fn_123. a)
there are ofc horribly broken or hackish ways of getting at the stack or the closure object (or if you have primitive support for continuations you don't need any of that), but decompilation is not what you're looking for
I think I was just confused about compiler passes and what gets compiled to its own class
The reason I was decompiling the cc specifically was because I assumed that would be a "horribly broken or hackish ways of getting at the stack or the closure object." As you mention, cps transform is a way of capturing the call stack but you can't inspect it because it itself is already compiled. I assume the final piece in this is that if I decompile it I'm still only getting at the top-level class, i.e. the implementation of the continuation rather than the actual frame objects. Is that right?
And looking at that decompiled continuation, I assume I would have to decompile several classes down to get to any clojure.lang.Var$Frame object.
>I assume the final piece in this is that if I decompile it I'm still only getting at the top-level class, i.e. the implementation of the continuation rather than the actual frame objects. yes
Ah gotcha. So I do have a third option, which is the truly perverse one: recursively disassembling the continuation until I reach frame objects.
I don't want to belabor that, though. The other thing I was going to ask was just about compiling tools.decompiler with javac. Their docs are actually rather sparse. Is there an example project file somewhere?
I was referring to this line in your README: "Use lein javac to AOT compile clojure.tools.decompiler.RetrieveClasses then you can use lein repl or clj to launch a repl"
ah, to compile tools.decompiler itself, sure, you literally just need to type lein javac
, what's the issue?
Ok. I think it was actually including that field in my own project.clj that screwed me up
Sorry, I'm multitasking and realize it's evening for you. I do really appreciate your help today. And everything you've done with these libraries is great and right up my alley. I should probably think about contributing in some form eventually.
Let me know if you'd rather me open an issue about this, but I had to revert to 1.8 due to the removal of bigdec?
and now for some odd reason this let binding isn't being resolved: https://github.com/Bronsa/tools.decompiler/blob/master/src/clojure/tools/decompiler/bc.clj#L120
i'm using clojure 1.9 and your version 0.1.0-alpha1. the error message makes it seems like the version on clojars was before this commit: https://github.com/Bronsa/tools.decompiler/commit/9f2c29492ed26182f96b805cde86583c362f6abc#diff-192c7acd38e01ad43d78f10f3b6adc1e
Is there a better way to set multiple fields on a java object than this?
(doto (Object.)
(-> (. -field1) (set! value1))
(-> (. -field2) (set! value2)))
I donβt think so but you could macro it
macros are for people who don't know the value of anything but are sure finding out costs too much

@hiredman speaking of macros, i spent way too many hours last night playing with that CPS library you linked me to
It uses the most basic type of CPS transform and doesn't implement delimited continuations or other common control operators, instead just replicating what Scheme had in the 80s and decided were bad ideas. I think the purpose was purely to push recursive calls that can't use recur
from the stack to the heap.
My initial assessment was just that it was good enough for the totally-not-practical toy project I wanted to spend a weekend on. The problem with that turns out not to be due to anything in that library but rather just basic differences in the first passes of the Clojure compiler vs. other Lisps.
Hence my questions above about decompilation...I literally started decompiling undelimited continuations using no.disassemble
and then realized how insane that was so naturally kept at it for hours figuring out ways to prune the output so I could even examine it π
I'm sure @bronsa could answer everything immediately, but I forgot he's in London and spent all afternoon on calls...including a videoconference with another Brit who kept yawning the whole time
when have folks found a good reason for using Java Executors over of clojure's built-in future
& soloExecutor?
no.disassemble
is a disassembler, it takes a class file and outputs symbolic bytecode, it doesn't do anything besides reading the binary class file and parsing it into clojure values - - tools. decompiler
is a decompiler : it includes a disassembler but also parses that symbolic bytecode back into a tools.analyzer
-like AST and it tries converting that back to pre-macroexpanded code
Hi, With clojure.spec, Is there a way to get all the errors with s/and instead of having only the first one ? e.g.
``
(s/explain (s/and int? even? #(> % 1000)) 9)
;; val: 9 fails predicate: even?
``
Is it possible to also have the last predicate error ? (> % 1000)
Many ThanksHey everyone! Does anyone know of a way to apply a function to specific nodes of XML? For example, apply inc
to every ...<number>1</number>...
element.
Not exactly what you're asking, but a transformation like that would be trivial with XSLT. For example:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="xml" indent="yes"/>
<!--
Copy every input node that doesn't match any other <xsl:template> into the
output XML tree as is.
-->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!--
Increment the value of every <number> element by one.
-->
<xsl:template match="number">
<xsl:copy>
<xsl:value-of select="xs:int(.) + 1"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Unfortunately the project is limited to using Clojure. Thanks though!
Ohh interesting. Thank you! π
If you want to stick with Clojure, another option would be to use Specter (https://github.com/nathanmarz/specter).
(use 'com.rpl.specter)
(transform (walker number?) inc xml-document)
=>
#xml/element{:tag :parent,
:content [#xml/element{:tag :number, :content [2]}
#xml/element{:tag :child, :content [#xml/element{:tag :number, :content [3]}]}]}
That worked, thank you! π
^ this is my current approach, but it seems to be returning two results (each result having the function applied to one node) instead of one result with the function being applied to all nodes. π
Is there a better way than (reverse (sort-by :something ,,,))
if the sorting comparator returns the βwrongβ order?
user=> (sort-by count ["aa" "aaa" "a"])
("a" "aa" "aaa")
user=> (sort-by count < ["aa" "aaa" "a"])
("a" "aa" "aaa")
user=> (sort-by count > ["aa" "aaa" "a"])
("aaa" "aa" "a")
How did I not know that sort-by has an additional arity π
i was looking at that page last night. it's not even in the list of conferences. be nice if there was some info like, "yes this will happen, dates TBD"
october or november @eraserhd is a safe bet, not guaranteed. I don't recall it ever being in December
thatβs like multi-level TBD :)
I'm doing a quick performance test, trying to justify using Clojure for a project, and I'm surprised by the results. I wondered if y'all could sanity check my approach.
In Clojure, I'm starting 100 futures which each print 1,000 lines, sleeping 1ms between each print. In Ruby, I'm doing essentially the same thing, except I'm using fork
instead of future
so I'm dealing with 100 OS processes instead of 100 JVM threads. To my surprise, the Ruby script outperforms the Clojure script: Ruby finishes in 1.24s, but Clojure over twice that, even after subtracting JVM startup time.
What's going on here? Am I wrong to expect that threads would be faster than processes?
os sees 1 jvm and schedules it accordingly so it gets 1/n timeshare (ish). (n is # of processess). the processess get combined (100)/(100 + n) timeshare
My system isn't under any loadβ¦wouldn't the OS pretty much give the JVM full access?
sleep resolution isn't guaranteed, printing has some peculiarities when multithreaded...
OK. Good point about I/O. I'll try converting the work each thread is doing to something numerical, with a print at the end to verify the result.
how does it compare when removing the sleep and printing to 100 diff. files instead? then I think it's a very valid benchmark, for this specific use case
I don't know @fa; I can try that. Why do you think that will be different?
I think it addresses @ghadiβs point about being a bad benchmark and in my eyes is still close enough
OK, removing the print from the inner loop, I'm getting 1.2s for Ruby and 1.55s for Clojure (excluding JVM overhead). Closer, but Clojure/threads are still slower than Ruby/processes.
I really don't want to use Ruby.
I might just decide not to, but I was hoping to have a decision more justifiable than mere preference. Β―\(γ)/Β―
(Thinking about this from the perspective of those over me who prefer OO approaches etc.)
@jeff.terrell the JVM will absolutely cream Ruby, but you'll have to wait until jitting. Also consider all the other benefits -- immutability, functional programming.
Ruby might in fact be a better proposition for your use case. Sometimes you know before a project starts, sometimes you learn it afterwards
the performance of any language is also going to depend on how well you understand how the whole system around the language works
@ghadi - Can you explain more about jitting? Can I AOT to determine how much of this time is due to jitting?
println
prints to out which is by default System/out which is I think a PrintWriter, which has certain buffer characteristics and a file encoding
Nice, thanks @hiredman. At the moment I'm only printing once per thread at the end and still seeing ~30% longer execution times in Clojure, so I doubt that's what I'm hitting now.
Good details though, thanks.
it has been a log time since I've touched ruby, but I doubt it is trying to do any text encoding when you call puts
are you printing to a terminal? you might be timing a terminal and not ruby or clojure/jvm
Ah, now I see why you said that. :-)
I'm printing to stdout but redirecting to a file via shell. Good point though. π
my point is without some deeper understanding of what is going on, it is unlikely you are making a sound comparison that will usefully inform decisions for your project
I can tell you that, when I worked at Groupon, some ruby projects that had to be highly concurrent were converted to JRuby. Everybody hated JRuby, but would agree that the JVM multithreads much better than the MRI.
not that I have ever seen a good benchmark influence decisions about what to use for a project, if someone with the authority to tell you what to use wants to use ruby it is unlikely that a benchmark is going to change their mind
IIRC, it was because of the packaging and deploy situation. I never worked on JRuby, but I think it wasn't just "bundle install" or "cap deploy". Stuff breaking, and dependencies failing to work on JRuby.
ahh, makes sense. IIRC at my old job we used Traveling Ruby (which is now dead) and locked down our versions pretty hard especially of native gems to work around that
Thanks, @eraserhd. That's the kind of thing that seems like the conventional wisdom for sure. And similarly, node.js doesn't do real parallelism either. So I was surprised to see this result for my little benchmark.
@hiredman - I think that's a great point, about the people side of this. I should have discretion on this project. I'm concerned about using up my persuasion capital on a tool that maybe isn't a great fit for Clojure, but maybe I should just worry about persuasion another day.
@jeff.terrell Writing into files might be IO bound too. How about you pick a realistic usecase and implement that in ruby / vs clojure and see how that compares? Picking something artificial hardly makes sense.
@sveri - Yeah, that's not a bad idea. We'll see if I have time for it. The goal here was to get a quick sense of how things compared without implementing the full solution.
if I saw above that your total test time is in the 1 second range, I donβt think this has any chance of telling you something interesting, given that Clojure start up times are ~1s
I did manual timing in my code to eliminate JVM startup overhead, but Ghadi pointed out that there is additional overhead from jitting that's hard to nail down. So yeah, that makes sense.
Exactly, try to find something that takes several minutes with ruby and then implement that thing in clojure.
Hi all! I've been learning Clojure(Script) casually in my spare time as a student for a few years, and I just got my first real paid gig writing Clojure (google summer of code). I've got a couple of good books but I'd like some more resources to make sure I'm not embarrassing myself with my first commits. Any suggestions as far as high-quality repos to look at (aside from core), or other online resources besides this channel?
@U1B1E4ZDM Application code and library code will, and should, look very different. My best advice would be to look at the code in web frameworks templates, such as luminus, chesnut, duct etc - they are designed to be exemplary application code.
Agreed. There's a huge difference between the style of code in my favorite libraries and anything you'd want to be writing on a production team. Since you mentioned ClojureScript in particular (and btw there's a channel dedicated to that) I would mention Om.Next as something I think is amazing and relatively few people use in production (Reagent/Re-Frame might be good to look at instead). Another example is Specter, which has quickly become probably my favorite Clojure library. It's by definition a non-idiomatic way to write Clojure and additionally the source is largely macros that have had a ton of performance tuning. That's the kind of stuff you wouldn't want to write in a setting like this.
I'd recommend looking at @U064WBEPL's blog and github for "best practices" type stuff.
@U06GS6P1N Thanks a lot, good point about application vs library style. And I'm a fan of Bhozidar, I'll have a look at that style guide.