Fork me on GitHub
#clojure
<
2018-04-24
>
sophiago00:04:57

Can anyone speak to the differences between no.disassemble and @bronsa's tools.decompiler?

bronsa08:04:56

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

quadron00:04:32

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?

sophiago00:04:04

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.

bronsa08:04:53

macros take an implicit &env parameter

sophiago17:04:30

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 πŸ˜›

simple_smile 4
sophiago17:04:55

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.

bronsa17:04:52

user=> (defmacro foo [] (list 'quote (keys &env)))
#'user/foo
user=> (foo)
nil
user=> (let [a 1] (foo))
(a)

sophiago18:04:12

Oh huh. That seems pretty much like a local version of let/cc in Scheme.

bronsa18:04:49

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

bronsa18:04:31

no, let/cc captures the current continuation, &env is just a mapping of the compiler lexical state

sophiago18:04:34

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.

sophiago18:04:02

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?

bronsa18:04:04

I'm not sure I understood your question

sophiago18:04:02

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.

sophiago18:04:41

I'm assuming now I'm discounting lets 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.

bronsa18:04:21

are you looking for :binding nodes?

sophiago18:04:26

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.

bronsa18:04:08

references to local variables are :op :local

bronsa18:04:54

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

sophiago18:04:57

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)?

bronsa18:04:18

fn, let, letfn, deftype/reify arguments, loop, catch

bronsa18:04:49

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

bronsa18:04:06

so in (let [a 1] a), the first a is a :binding, the second is a :local

sophiago18:04:15

That sounds like everything I should need.

sophiago18:04:42

The example I was going to use is something like:

sophiago18:04:58

(defn succ   [n] (fn [f] (fn [x] (f ((n f) x)))))
=> {:n {:f {:x [(f ((n f) x))]}}}

bronsa18:04:59

the :env val holds all the local variables that are available at this point + metadata about the node

sophiago18:04:38

Oh, okay. So my confusion was looking at :env in reverse order. I should have been going from the inner most scope out.

sophiago18:04:08

(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)

sophiago18:04:23

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.

bronsa18:04:52

"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

bronsa18:04:10

you won't have access to locals by looking at the static bytecode for a funcntion

bronsa18:04:34

let me know if there's some better documentation that would've helped you figure this out earlier

sophiago18:04:30

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

sophiago18:04:24

The downside of not being forced to take that ludicrous approach is I wanted to title a blog post on this "Decompiling with Continuations"

sophiago18:04:08

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.

bronsa18:04:51

>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

bronsa18:04:03

if you're decompiling the class you're not seeing any property about the runtime object of that function

bronsa18:04:18

like the closure objects

sophiago18:04:26

Yup, I realized it was a Clojure vs. Scheme issue in my expectations and had a hunch about what you just told me.

bronsa18:04:10

nah it's not a clojure vs scheme, it's that that you were asking a completely different question to clojure

bronsa18:04:41

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

bronsa18:04:36

hopefully this cleared up a bit of the confusion

sophiago18:04:59

Well, first of all I just wanted to comment on something previously.

sophiago18:04:01

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."

sophiago18:04:32

Just since you asked: relatively the tools.analyzer docs are quite good.

bronsa18:04:33

>I'd have to reconstruct the binding sites from the :env :locals {}, right yes

bronsa18:04:05

ok, I'll try to write a bit more of a tutorial-like introduction when I have some time

sophiago18:04:35

Got it. I was unclear whether that information would be preserved in the keys to the hash-map somehow.

bronsa18:04:36

I did give a talk a few years ago about some of the main ideas in tools.analyzer if you're interested

bronsa18:04:31

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

sophiago18:04:33

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.

bronsa18:04:48

it doesn't break homoiconicity

bronsa18:04:07

homoiconicity doesn't mean that the shape of the code is exactly the shape of the AST

sophiago18:04:14

I just mean that the ASTs aren't simply the s-expressions in user land.

bronsa18:04:29

they almost never are, even in a lot of schemes

bronsa18:04:04

e.g. in the nanopass framework the AST is a record hidden behind a syntax-object

sophiago18:04:22

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.

sophiago19:04:22

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.

sophiago19:04:46

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."

bronsa19:04:44

when you decompile, say (let [a 1] (fn [] a))

bronsa19:04:04

that (fn [] a) needs to be first compiled into executable code

bronsa19:04:10

then instantiated for a=1

bronsa19:04:29

when you decompile it, you get the machine/bytecode for the (fn [] a) function

bronsa19:04:45

but you don't get the closure associated with the intance with a=1 or its stack

bronsa19:04:52

is this clearer?

bronsa19:04:01

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))

bronsa19:04:09

and you're decompiling the my_fn_123 type/class, not anything about any of its instances, like (my_fn_123. a)

bronsa19:04:25

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

bronsa19:04:54

(csp transform is one way to reify and access the call stack )

sophiago19:04:02

Sorry, had to take a call and just reading this

sophiago19:04:49

I think I was just confused about compiler passes and what gets compiled to its own class

sophiago19:04:10

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?

sophiago19:04:59

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.

bronsa19:04:22

>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

sophiago19:04:46

Ah gotcha. So I do have a third option, which is the truly perverse one: recursively disassembling the continuation until I reach frame objects.

bronsa19:04:43

the frame objects are not code you can decompile

bronsa19:04:50

they are runtime values

sophiago19:04:01

Ah ok. They're basically just hooks

sophiago19:04:14

So I can use them, but not inspect them

sophiago19:04:45

I still wonder if there's a way I could inspect them through instrumentation

sophiago19:04:56

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?

bronsa19:04:49

>compiling tools.decompiler with javac

sophiago20:04:27

I was under the assumption it has to be AOT compiled using lein-javac

bronsa20:04:53

javac is for java files

bronsa20:04:06

tools.decompiler works on clojure compiled forms

sophiago20:04:48

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"

bronsa20:04:29

ah, to compile tools.decompiler itself, sure, you literally just need to type lein javac, what's the issue?

sophiago20:04:11

i don't need anything in :javac-source-path?

sophiago20:04:43

Ok. I think it was actually including that field in my own project.clj that screwed me up

sophiago20:04:02

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.

bronsa20:04:30

you're welcome simple_smile

sophiago21:04:42

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

bronsa19:04:59

that's because of a 1.8 bug

bronsa19:04:08

tools.decompiler only supports 1.9

bronsa19:04:18

bigdec? is not in 1.8 either

bronsa19:04:29

it was just in some alpha versions of 1.9 so I don't know what issue you're having

bronsa19:04:26

1.9 is backwards compatible with 1.8

sophiago06:04:44

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

Garrett Hopper00:04:44

Is there a better way to set multiple fields on a java object than this?

(doto (Object.)
  (-> (. -field1) (set! value1))
  (-> (. -field2) (set! value2)))

Alex Miller (Clojure team)00:04:06

I don’t think so but you could macro it

hiredman00:04:27

macros are for people who don't know the value of anything but are sure finding out costs too much

metal 4
hiredman00:04:58

by which I mean, you could also use reflection

πŸ‘ 4
sophiago00:04:05

@hiredman speaking of macros, i spent way too many hours last night playing with that CPS library you linked me to

hiredman00:04:49

is it any good?

sophiago00:04:17

Strictly speaking, no. But compared to alternatives in Clojure? Well..

sophiago01:04:38

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.

sophiago01:04:18

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.

sophiago01:04:49

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 πŸ˜›

sophiago01:04:10

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

rymndhng04:04:52

when have folks found a good reason for using Java Executors over of clojure's built-in future & soloExecutor?

chrisblom13:04:00

i’ve used java executors when i wanted control the max number of threads

chrisblom13:04:07

and also for monitoring purposes

bronsa08:04:56

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

ouvasam09:04:47

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 Thanks

Volodymyr Sereda10:04:52

Hey 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.

flowthing10:04:32

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>

Volodymyr Sereda10:04:47

Unfortunately the project is limited to using Clojure. Thanks though!

Volodymyr Sereda10:04:26

Ohh interesting. Thank you! πŸ™‚

flowthing10:04:16

If you want to stick with Clojure, another option would be to use Specter (https://github.com/nathanmarz/specter).

flowthing10:04:48

(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]}]}]}

Volodymyr Sereda11:04:15

That worked, thank you! πŸ™

Volodymyr Sereda10:04:49

^ 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. πŸ™‚

martinklepsch12:04:22

Is there a better way than (reverse (sort-by :something ,,,)) if the sorting comparator returns the β€œwrong” order?

souenzzo12:04:29

@martinklepsch

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")

martinklepsch12:04:14

duh, that’s great! πŸ˜…

πŸ‘ 4
martinklepsch12:04:45

How did I not know that sort-by has an additional arity πŸ˜„

eraserhd12:04:26

Is there an approximate time for Clojure/conj this year?

dpsutton13:04:05

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"

ghadi13:04:50

october or november @eraserhd is a safe bet, not guaranteed. I don't recall it ever being in December

bronsa13:04:37

2016 was december

bronsa13:04:49

it coincided with clojureX in london

ghadi13:04:04

never mind!

Alex Miller (Clojure team)13:04:00

@dpsutton yes, it will happen, dates TBD

aw_yeah 4
borkdude14:04:52

I’m already looking forward to RH’s TBD talk (if that will be on the program)

Alex Miller (Clojure team)14:04:25

that’s like multi-level TBD :)

jeff.terrell17:04:44

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?

dpsutton17:04:57

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

dpsutton17:04:46

so the balance is context switching versus how much cpu time they get

jeff.terrell17:04:53

My system isn't under any load…wouldn't the OS pretty much give the JVM full access?

dpsutton17:04:56

and i could be totally wrong. but just my naive take

ghadi17:04:28

this is a really bad benchmark

ghadi17:04:13

sleep resolution isn't guaranteed, printing has some peculiarities when multithreaded...

dpsutton17:04:14

i think we're only trying to understand the discrepancy from expectation and result

dpsutton17:04:51

good point on the printing. didn't think of that

ghadi17:04:11

not waiting for JITting...

jeff.terrell17:04:12

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.

wink17:04:36

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

jeff.terrell17:04:00

I don't know @fa; I can try that. Why do you think that will be different?

wink17:04:15

I think it addresses @ghadi’s point about being a bad benchmark and in my eyes is still close enough

jeff.terrell17:04:47

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.

jeff.terrell17:04:36

I really don't want to use Ruby. simple_smile

jeff.terrell17:04:30

I might just decide not to, but I was hoping to have a decision more justifiable than mere preference. Β―\(ツ)/Β―

jeff.terrell17:04:58

(Thinking about this from the perspective of those over me who prefer OO approaches etc.)

ghadi17:04:21

@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.

ghadi17:04:24

Ruby might in fact be a better proposition for your use case. Sometimes you know before a project starts, sometimes you learn it afterwards

hiredman17:04:42

the performance of any language is also going to depend on how well you understand how the whole system around the language works

ghadi17:04:14

proselytizing beforehand can make people less open-minded

πŸ‘ 4
jeff.terrell17:04:13

@ghadi - Can you explain more about jitting? Can I AOT to determine how much of this time is due to jitting?

hiredman17:04:29

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

ghadi17:04:31

the JVM will compile methods after 10000 invocation, IIRC

hiredman17:04:46

all of which is going to effect the speed of printing strings to it

jeff.terrell17:04:43

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.

jeff.terrell17:04:50

Good details though, thanks.

hiredman17:04:03

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

dpsutton17:04:19

are you printing to a terminal? you might be timing a terminal and not ruby or clojure/jvm

dpsutton17:04:27

terminal rendering can be very slow

wink17:04:38

that's why I said 100 files πŸ˜›

jeff.terrell17:04:29

Ah, now I see why you said that. :-)

jeff.terrell17:04:59

I'm printing to stdout but redirecting to a file via shell. Good point though. πŸ‘

hiredman17:04:07

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

eraserhd17:04:53

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.

hiredman17:04:41

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

schmee17:04:54

out of curiosity: why did everyone hate JRuby?

eraserhd17:04:59

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.

schmee18:04:51

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

jeff.terrell17:04:04

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.

jeff.terrell17:04:23

@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.

the2bears18:04:35

@jeff.terrell it's easier to ask forgiveness than to ask permission πŸ˜‰

πŸ˜† 4
sveri18:04:32

@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.

jeff.terrell18:04:46

@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.

Alex Miller (Clojure team)18:04:08

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

jeff.terrell18:04:10

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.

sveri18:04:02

Exactly, try to find something that takes several minutes with ruby and then implement that thing in clojure.

jmacneal19:04:51

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?

πŸ‘ 4
val_waeselynck07:04:59

@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.

sophiago08:04:34

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.

sophiago08:04:47

I'd recommend looking at @U064WBEPL's blog and github for "best practices" type stuff.

jmacneal19:04:54

@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.

jmacneal19:04:21

@U2TCUSM2R I'll check it out thank you.

πŸ‘ 4