Clojure JIRAhttp://dev.clojure.org/jira/secure/IssueNavigator.jspa?reset=true&jqlQuery=project+%3D+CLJ+AND+resolution+%3D+Unresolved+AND+fixVersion+%3D+Backlog+ORDER+BY+priority+DESC
An XML representation of a search requesten-us4.464925-07-2011[CLJ-1517] Unrolled small vectorshttp://dev.clojure.org/jira/browse/CLJ-1517
Clojure<p>As discussed on the mailing list <span class="error">&#91;1&#93;</span>, this patch has two unrolled variants of vectors and maps, with special inner classes for each cardinality. Currently both grow to six elements before spilling over into the general versions of the data structures, which is based on rough testing but can be easily changed. At Rich's request, I haven't included any integration into the rest of the code, and there are top-level static create() methods for each.</p>
<p>The sole reason for this patch is performance, both in terms of creating data structures and performing operations on them. This can be seen as a more verbose version of the trick currently played with PersistentArrayMap spilling over into PersistentHashMap. Based on the benchmarks, which can be run by cloning cambrian-collections <span class="error">&#91;2&#93;</span> and running 'lein test :benchmark', this should supplant PersistentArrayMap. Performance is at least on par with PAM, and often much faster. Especially noteworthy is the creation time, which is 5x faster for maps of all sizes (lein test :only cambrian-collections.map-test/benchmark-construction), and on par for 3-vectors, but 20x faster for 5-vectors. There are similar benefits for hash and equality calculations, as well as calls to reduce().</p>
<p>This is a big patch (over 5k lines), and will be kind of a pain to review. My assumption of correctness is based on the use of collection-check, and the fact that the underlying approach is very simple. I'm happy to provide a high-level description of the approach taken, though, if that will help the review process.</p>
<p>I'm hoping to get this into 1.7, so please let me know if there's anything I can do to help accomplish that.</p>
<p><span class="error">&#91;1&#93;</span> <a href="https://groups.google.com/forum/#!topic/clojure-dev/pDhYoELjrcs">https://groups.google.com/forum/#!topic/clojure-dev/pDhYoELjrcs</a><br/>
<span class="error">&#91;2&#93;</span> <a href="https://github.com/ztellman/cambrian-collections">https://github.com/ztellman/cambrian-collections</a></p>
<p><b>Patch</b>: unrolled-vector-2.patch</p>
<p><b>Screener Notes</b>: The approach is clear and understandable. Given the volume of generated code, I believe that best way to improve confidence in this code is to get people using it asap, and add collection-test <span class="error">&#91;3&#93;</span> to the Clojure test suite. I would also like to get the generator <span class="error">&#91;4&#93;</span> included in the Clojure repo. We don't need to necessarily automate running it, but would be nice to have it nearby if we want to tweak something later.</p>
<p><span class="error">&#91;3&#93;</span> <a href="https://github.com/ztellman/collection-check/blob/master/src/collection_check.clj">https://github.com/ztellman/collection-check/blob/master/src/collection_check.clj</a><br/>
<span class="error">&#91;4&#93;</span> <a href="https://github.com/ztellman/cambrian-collections/blob/master/generate/cambrian_collections/vector.clj">https://github.com/ztellman/cambrian-collections/blob/master/generate/cambrian_collections/vector.clj</a></p>CLJ-1517Unrolled small vectorsEnhancementCriticalOpenUnresolvedUnassignedZach TellmancollectionsperformanceMon, 1 Sep 2014 21:53:17 -0500Fri, 31 Jul 2015 17:14:47 -0500Release 1.7Backlog2017<p>Oh, I forgot to mention that I didn't make a PersistentUnrolledSet, since the existing wrappers can use the unrolled map implementation. However, it would be moderately faster and more memory efficient to have one, so let me know if it seems worthwhile.</p><p>Zach, the patch you added isn't in the correct format, they need to be created using `git format-patch`</p><p>Also, I'm not sure if this is on-scope with the ticket but those patches break with &#42;print-dup&#42;, as it expects a static create&#40;x&#41; method for each inner class.</p>
<p>I'd suggest adding a create(Map x) static method for the inner PersistentUnrolledMap classes and a create(ISeq x) one for the inner PersistentUnrolledVector classes</p><p>Re making patches, see: <a href="http://dev.clojure.org/display/community/Developing+Patches">http://dev.clojure.org/display/community/Developing+Patches</a></p><p>I wonder what is the overhead of having meta and 2 hash fields in the class. Have you considered a version where the hash is computed on the fly and where you have two sets of collections, one with meta field and one without, using former when the actual metadata is attached to the collection?</p><p>I've attached a patch using the proper method. Somehow I missed the detailed explanation for how to do this, sorry. I know the guidelines say not to delete previous patches, but since the first one isn't useful I've deleted it to minimize confusion.</p>
<p>I did the <b>print-dup</b> friendly create methods, and then realized that once these are properly integrated, 'pr' will just emit these as vectors. I'm fairly sure the create methods aren't necessary, so I've commented them out, but I'm happy to add them back in if they're useful for some reason I can't see.</p>
<p>I haven't given a lot of thought to memory efficiency, but I think caching the hashes are worthwhile. I can see an argument for creating a "with-meta" version of each collection, but since that would double the size of an already enormous patch, I think that should probably wait.</p><p>I found a bug! Like PersistentArrayMap, I have a special code path for comparing keywords, but my generators for collection-check were previously using only integer keys. There was an off-by-one error in the transient map implementation <span class="error">&#91;1&#93;</span>, which was not present for non-keyword lookups.</p>
<p>I've taken a close look for other gaps in my test coverage, and can't find any. I don't think this substantively changes the risk of this patch (an updated version of which has been uploaded as 'unrolled-collections-2.diff'), but obviously where there's one bug, there may be others. </p>
<p><span class="error">&#91;1&#93;</span> <a href="https://github.com/ztellman/cambrian-collections/commit/eb7dfe6d12e6774512dbab22a148202052442c6d#diff-4bf78dbf5b453f84ed59795a3bffe5fcR559">https://github.com/ztellman/cambrian-collections/commit/eb7dfe6d12e6774512dbab22a148202052442c6d#diff-4bf78dbf5b453f84ed59795a3bffe5fcR559</a></p><p>As an additional data point, I swapped out the data structures in the Cheshire JSON library. On the "no keyword-fn decode" benchmark, the current implementation takes 6us, with the unrolled data structures takes 4us, and with no data structures (just lexing the JSON via Jackson) takes 2us. Other benchmarks had similar results. So at least in this scenario, it halves the overhead.</p>
<p>Benchmarks can be run by cloning <a href="https://github.com/dakrone/cheshire">https://github.com/dakrone/cheshire</a>, unrolled collections can be tested by using the 'unrolled-collections' branch. The pure lexing benchmark can be reproduced by messing around with the cheshire.parse namespace a bit.</p><p>Is there no way to get this into 1.7? It's an awfully big win to push off for another year.</p><p>Hey Zach, it's definitely considered important but we have decided to drop almost everything not fully done for 1.7. Timeframe for following release is unknown, but certainly expected to be significantly less than a year. <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/></p><p>You are all free to determine the time table, but I thought I'd point out that Zach is not entirely off-base. Clojure 1.4.0 was released April 5th, 2012. Clojure 1.5.0 was released March 1st, 2013 with 1.6.0 showing up March 25th, 2014. So it appears that the current cadence is around a year.</p><p>John, there is no point to comments like this. Let's please keep issue comments focused on the issue.</p><p>I did a small write-up on this patch which should help in the eventual code review: <a href="http://blog.factual.com/using-clojure-to-generate-java-to-reimplement-clojure">http://blog.factual.com/using-clojure-to-generate-java-to-reimplement-clojure</a></p><p>Per my conversation with Alex at the Conj, here's a patch that only contains the unrolled vectors, and uses the more efficient constructor for PersistentVector when spilling over.</p><p>Zach, I created a new placeholder for the map work at <a href="http://dev.clojure.org/jira/browse/CLJ-1610">http://dev.clojure.org/jira/browse/CLJ-1610</a>.</p><p>It should probably be noted that <tt>core.rrb-vector</tt> will break for small vectors by this patch, as it peeks into the underlying structure. This will also break other libraries which peeks into the vector implementation internals, although I'm not aware of any other – certainly not any other contrib library. </p>
<p>Also, two comments on <tt>unrolled-vector.patch</tt>:</p>
<p><tt>private <b>transient</b> boolean edit = true;</tt><br/>
in the Transient class should probably be<br/>
<tt>private <b>volatile</b> boolean edit = true;</tt><br/>
as transient means something entirely different in Java.</p>
<p><tt>conj</tt> in the <tt>Transient</tt> implementation <em>could</em> invalidate itself without any problems (<tt>edit = false;</tt>) if it is converted into a TransientVector (i.e. spills over) – unless it has a notable overhead. The invalidation can prevent some subtle bugs related to erroneous transient usage.</p><p>Jean - understanding the scope of the impact will certainly be part of the integration process for this patch. I appreciate the heads-up. While we try to minimize breakage for things like this, it may be unavoidable for libraries that rely on implementation internals.</p><p>I'll add support for unrolled vectors to <tt>core.rrb-vector</tt> the moment they land on master. <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/> (Probably with some conditional compilation so as not to break compatibility with earlier versions of Clojure – we'll see when the time comes.)</p><p>I should say that it'd be possible to add generic support for any "vector lookalikes" by pouring them into regular vectors in linear time. At first glance it seems to me that that'd be out of line with the basic promise of the library, but I'll give it some more thought before the changes actually land.</p><p>Somewhat predictably, the day after I cut the previous patch, someone found an issue <span class="error">&#91;1&#93;</span>. In short, my use of the ArrayChunk wrapper applied the offset twice.</p>
<p>This was <b>not</b> caught by collection-check, which has been updated to catch this particular failure. It was, however, uncovered by Michael Blume's attempts to merge the change into Clojure, which tripped a bunch of alarms in Clojure's test suite. My own attempt to do the same to "prove" that it worked was before I added in the chunked seq functionality, hence this issue persisting until now.</p>
<p>As always, there may be more issues lurking. I hope we can get as many eyeballs on the code between now and 1.8 as possible. </p>
<p><span class="error">&#91;1&#93;</span> <a href="https://github.com/ztellman/cambrian-collections/commit/2e70bbd14640b312db77590d8224e6ed0f535b43">https://github.com/ztellman/cambrian-collections/commit/2e70bbd14640b312db77590d8224e6ed0f535b43</a><br/>
<span class="error">&#91;2&#93;</span> <a href="https://github.com/MichaelBlume/clojure/tree/test-vector">https://github.com/MichaelBlume/clojure/tree/test-vector</a></p><p>As a companion to the performance analysis in the unrolled map issue, I've run the benchmarks and posted the results at <a href="https://gist.github.com/ztellman/10e8959501fb666dc35e">https://gist.github.com/ztellman/10e8959501fb666dc35e</a>. Some notable results:</p>
<ul>
<li>`conj` is 3x faster than generic vectors: <a href="https://gist.github.com/ztellman/10e8959501fb666dc35e#file-gistfile1-txt-L329-L451">https://gist.github.com/ztellman/10e8959501fb666dc35e#file-gistfile1-txt-L329-L451</a></li>
<li>`reduce +` is 3x faster than generic vectors: <a href="https://gist.github.com/ztellman/10e8959501fb666dc35e#file-gistfile1-txt-L795-L887">https://gist.github.com/ztellman/10e8959501fb666dc35e#file-gistfile1-txt-L795-L887</a></li>
</ul>
<p>Stu: I do not think this patch should be marked "screened" until the actual integration and build work (if the generator is integrated) has been completed.</p><p>FYI, we have "reset" all big features for 1.8 for the moment (except the socket repl work). We may still include it - that determination will be made later.</p><p>Okay, any idea when the determination will be made? I was excited that we seemed to be finally moving forward on this.</p><p>No, but it is now on my work list.</p><p>I wonder if all of the overriding of APersistentVector yields important benefits - e.g. iterator, hashcode etc.</p><p>In the case of hashcode, definitely: <a href="https://gist.github.com/ztellman/10e8959501fb666dc35e#file-gistfile1-txt-L1013-L1076">https://gist.github.com/ztellman/10e8959501fb666dc35e#file-gistfile1-txt-L1013-L1076</a>. This was actually one of the original things I wanted to speed up.</p>
<p>In the case of the iterator, probably not. I'd be fine removing that.</p>
<p>So am I to infer from <a href="https://github.com/clojure/clojure/commit/36d665793b43f62cfd22354aced4c6892088abd6">https://github.com/clojure/clojure/commit/36d665793b43f62cfd22354aced4c6892088abd6</a> that this issue is defunct? If so, I think there's a lot of improvements being left on the table for no particular reason.</p><p>Yes, that commit covers this functionality. It takes a different approach from the patch in building up from a small core, and maximizing improvements to the bases rather than having a lot of redundant definitions per class. That also allowed for immediate integration without as much concern for correctness, as there is little new code. It also emphasizes the use case for tuples, e.g. small vectors used as values that won't be changed, thus de-emphasizing the 'mutable' functions. I disagree that many necessary improvements are being left out. The patch 'optimized' many things that don't matter. Further, there are not big improvements to the pervasive inlining. In addition, the commit includes the integration work at a fraction of the size of the patch. In all, it would have taken much more back and forth to get the patch to conform with this approach than to just get it all done, but I appreciate the inspiration and instigation - thanks!</p><p>That said, this commit need not be the last word - it can serve as a baseline for further optimization. But I'd rather that be driven by need. Clojure could become 10x as large optimizing things that don't matter.</p><p>What is our reference for "relevant" performance? I (or anyone else) can provide microbenchmarks for calculating hashes or whatever else, but that doesn't prove that it's an important improvement. I previously provided benchmarks for JSON decoding in Cheshire, but that's just one of many possible real-world benchmarks. It might be useful to have an agreed-upon list of benchmarks that we can use when debating what is and isn't useful.</p><p>I was interested in this implementation so created a branch that integrates Zach's unrolled vectors on top of clojure 1.8.0-alpha2. I also simplified some of the code (I don't think the metadata handling or unrolled seqs are worthwhile, for example)</p>
<p>Github branch: <a href="https://github.com/mikera/clojure/tree/clj-1517">https://github.com/mikera/clojure/tree/clj-1517</a></p>
<p>Then I ran a set of micro-benchmarks created by Peter Taoussanis</p>
<p>Results: <a href="https://gist.github.com/mikera/72a739c84dd52fa3b6d6">https://gist.github.com/mikera/72a739c84dd52fa3b6d6</a></p>
<p>My findings from this testing:</p>
<ul class="alternate" type="square">
<li>Performance is comparable (within +/- 20%) on the majority of tests</li>
<li>Zach's approach is noticeably faster (by 70-150%) for 4 operations (reduce, mapv, into, equality)</li>
</ul>
<p>My view is that these additional optimisations are worthwhile. In particular, I think that reduce and into are very important operations. I also care about mapv quite a lot for core.matrix (It's fundamental to many numerical operations on arrays implemented using Clojure vectors). </p>
<p>Happy to create a patch along these lines if it would be acceptable.</p>
<p>The `reduce` improvements are likely due to the unrolled reduce and kvreduce impls, but the others are probably because of the unrolled transient implementation. The extra code required to add these would be pretty modest.</p><p>I actually condensed the code down to a single implementation for `Transient` and `TupleSeq`. I don't think these really need to be fully unrolled for each Tuple type. That helps by making the code even smaller (and probably also helps performance, given JVM inline caching etc.)</p><p>Hey folks,</p>
<p>Silly question: is there actually a particular set of reference benchmarks that everyone's currently using to test the work on tuples? It took me a while to notice how bad the variance was with my own set of micro benchmarks.</p>
<p>Bumping all the run counts up till the noise starts ~dying down, I'm actually seeing numbers now that don't seem to agree with others here <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/help_16.gif" height="16" width="16" align="absmiddle" alt="" border="0"/>.</p>
<p>Google Docs link: <a href="https://docs.google.com/spreadsheets/d/1QHY3lehVF-aKrlOwDQfyDO5SLkGeb_uaj85NZ7tnuL0/edit?usp=sharing">https://docs.google.com/spreadsheets/d/1QHY3lehVF-aKrlOwDQfyDO5SLkGeb_uaj85NZ7tnuL0/edit?usp=sharing</a><br/>
gist with the benchmarks: <a href="https://gist.github.com/ptaoussanis/0a294809bc9075b6b02d">https://gist.github.com/ptaoussanis/0a294809bc9075b6b02d</a></p>
<p>Thanks, cheers! <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/></p><p>Hey Peter, I can't reproduce your results, and some of them are so far off what I'd expect that I have to think there was some data gathering error. For instance, the assoc operation being slower is kind of inexplicable, considering the unrolled version doesn't do any copying, etc. Also, all of your numbers are significantly slower than the ones on my 4 year old laptop, which is also a bit strange.</p>
<p>Just to make sure that we're comparing apples to apples, I've adapted your benchmarks into something that pretty-prints the mean runtime and variance for 1.7, 1.8-alpha2, and Mike's 1517 fork. It can be found at <a href="https://github.com/ztellman/tuple-benchmark">https://github.com/ztellman/tuple-benchmark</a>, and the results of a run at <a href="https://gist.github.com/ztellman/3701d965228fb9eda084">https://gist.github.com/ztellman/3701d965228fb9eda084</a>.</p><p>Hey Zach just looked at your benchmarks and they are definitely more consistent with what I am seeing. The overall nanosecond timings look about right from my experience with similar code (e.g. working with small vectors in vectorz-clj).</p><p>Hi Zach, thanks for that!</p>
<p>Have updated the results -<br/>
Gist: <a href="https://gist.github.com/ptaoussanis/0a294809bc9075b6b02d">https://gist.github.com/ptaoussanis/0a294809bc9075b6b02d</a><br/>
Google docs: <a href="https://goo.gl/khgT83">https://goo.gl/khgT83</a></p>
<p>Note that I've added an extra sheet/tab to the Google doc for your own numbers at <a href="https://gist.github.com/ztellman/3701d965228fb9eda084">https://gist.github.com/ztellman/3701d965228fb9eda084</a>.</p>
<p>Am still struggling to produce results that show any consistent+significant post-JIT benefit to either of the tuple implementations against the micro benchmarks and one larger small-vec-heavy system I had handy.</p>
<p>It's looking to me like it's maybe possible that the JIT's actually optimising away most of the non-tuple inefficiencies in practice?</p>
<p>Of course it's <b>very</b> possible that my results are off, or my expectations wrong. The numbers have been difficult to pin down.</p>
<p>It definitely helped to have a standardised reference micro benchmark to work against (<a href="https://github.com/ztellman/tuple-benchmark">https://github.com/ztellman/tuple-benchmark</a>). Could I perhaps suggest a similar reference <b>macro</b> benchmark (maybe something from core.matrix, Mike?)</p>
<p>Might also be a good idea to define a worthwhile target performance delta for ops like these that run in the nanosecond range (or for the larger reference macro benchmark)?</p>
<p>Just some thoughts from someone passing through in case they're at all useful; know many of you have been deeply involved in this for some time so please feel free to ignore any input that's not helpful <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/></p>
<p>Appreciate all the efforts, cheers!</p><p>I think everyone should back off on their enthusiasm for this approach. After much analysis, I am seeing definite negative impacts to tuples, especially the multiple class approach proposed by Zach. What happens in real code is that the many tuple classes cause call sites that see different sized vectors to become megamorphic, and nothing gets adequately optimized. In particular, call sites that will see tuple-sized <b>and</b> large vectors (i.e. a lot of library code) will optimize differently depending upon which they see often first. So, if you run your first tight loop on vector code that sees tuples, that code could later do much worse (50-100%) on large vectors than before the tuples patch was in place. Being much slower on large collections is a poor tradeoff for being slightly faster on small ones.</p>
<p>Certainly different tuple classes for arity 0-6 is a dead idea. You get as good or better optimization (at some cost in size) from a single class e.g. one with four fields, covering sizes 0-4. I have a working version of this in a local branch. It is better in that sites that include pvectors are only bi-morphic, but I am still somewhat skittish given what I've seen.</p>
<p>The other takeaway is that the micro benchmarks are nearly worthless for detecting these issues.</p><p>I'm pretty sure that all of my real-world applications of the tuples (via clj-tuple) have been fixed cardinality, and wouldn't have surfaced any such issue. Thanks for putting the idea through its paces.</p><p>Rich these are good insights - do you have a benchmark that you are using as representative of real world code?</p>
<p>I agree that it is great if we can avoid call sites becoming megamorphic, though I also believe the ship has sailed on that one already when you consider the multiple types of IPersistentVector that already exist (MapEntry, PersistentVector, SubVector plus any library-defined IPersistentVector instances such as clojure.core.rrb-vector). As a consequence, the JVM is usually not going to be able to prove that a specific IPersistentVector interface invocation is monomorphic, which is when the really big optimisations happen.</p>
<p>In most of the real world code that I've been working with, the same size/type of vector gets used repeatedly (Examples: iterating over map entries, working with a sequence of size-N vectors), so in such cases we should be able to rely on the polymorphic inline cache doing the right thing.</p>
<p>The idea of a single Tuple class for sizes 0-4 is interesting, though I can't help thinking that a lot of the performance gain from this may stem from the fact that a lot of code does stuff like (reduce conj [] .....) or the transient equivalent which is a particularly bad use case for Tuples, at least from the call site caching perspective. There may be a better way to optimise such cases rather than simply trying to make Tuples faster.... e.g. calling asTransient() on a Tuple0 could perhaps switch straight into the PersistentVector implementation.</p>ApprovalIncompleteGlobal RankPatchCode[CLJ-47] GC Issue 43: Dead code in generated bytecodehttp://dev.clojure.org/jira/browse/CLJ-47
Clojure<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">Reported by Levente.Santha, Jan 11, 2009
The bug was described in detail in <span class="code-keyword">this</span> thread: http:<span class="code-comment">//groups.google.com/
</span>group/clojure/browse_thread/thread/81ba15d7e9130441
For clojure.core$last__2954.invoke the correct bytecode would be (notice
the removed <span class="code-quote">"<span class="code-keyword">goto</span> 65"</span> after <span class="code-quote">"41: <span class="code-keyword">goto</span> 0"</span>):
<span class="code-keyword">public</span> java.lang.<span class="code-object">Object</span> invoke(java.lang.<span class="code-object">Object</span>) <span class="code-keyword">throws</span>
java.lang.Exception;
Code:
0: getstatic #22; <span class="code-comment">//Field const__0:Lclojure/lang/Var;
</span> 3: invokevirtual #37; <span class="code-comment">//Method clojure/lang/Var.get:()Ljava/lang/
</span><span class="code-object">Object</span>;
6: checkcast #39; <span class="code-comment">//class clojure/lang/IFn
</span> 9: aload_1
10: invokeinterface #41, 2; <span class="code-comment">//InterfaceMethod clojure/lang/IFn.invoke:
</span>(Ljava/lang/<span class="code-object">Object</span>;)Ljava/lang/<span class="code-object">Object</span>;
15: dup
16: ifnull 44
19: getstatic #47; <span class="code-comment">//Field java/lang/<span class="code-object">Boolean</span>.FALSE:Ljava/lang/
</span><span class="code-object">Boolean</span>;
22: if_acmpeq 45
25: getstatic #22; <span class="code-comment">//Field const__0:Lclojure/lang/Var;
</span> 28: invokevirtual #37; <span class="code-comment">//Method clojure/lang/Var.get:()Ljava/lang/
</span><span class="code-object">Object</span>;
31: checkcast #39; <span class="code-comment">//class clojure/lang/IFn
</span> 34: aload_1
35: invokeinterface #41, 2; <span class="code-comment">//InterfaceMethod clojure/lang/IFn.invoke:
</span>(Ljava/lang/<span class="code-object">Object</span>;)Ljava/lang/<span class="code-object">Object</span>;
40: astore_1
41: <span class="code-keyword">goto</span> 0
44: pop
45: getstatic #26; <span class="code-comment">//Field const__1:Lclojure/lang/Var;
</span> 48: invokevirtual #37; <span class="code-comment">//Method clojure/lang/Var.get:()Ljava/lang/
</span><span class="code-object">Object</span>;
51: checkcast #39; <span class="code-comment">//class clojure/lang/IFn
</span> 54: aload_1
55: aconst_null
56: astore_1
57: invokeinterface #41, 2; <span class="code-comment">//InterfaceMethod clojure/lang/IFn.invoke:
</span>(Ljava/lang/<span class="code-object">Object</span>;)Ljava/lang/<span class="code-object">Object</span>;
62: areturn
Our JIT reported incorrect stack size along the basic block introduced by
the unneeded <span class="code-keyword">goto</span>.
The bug was present in SVN rev 1205.</pre>
</div></div>CLJ-47GC Issue 43: Dead code in generated bytecodeDefectMajorOpenUnresolvedUnassignedNoneWed, 17 Jun 2009 15:18:00 -0500Mon, 26 Aug 2013 08:05:30 -0500Backlog00<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/47">http://www.assembla.com/spaces/clojure/tickets/47</a></p><p>richhickey said: Updating tickets (#8, #19, #30, #31, #126, #17, #42, #47, #50, #61, #64, #69, #71, #77, #79, #84, #87, #89, #96, #99, #103, #107, #112, #113, #114, #115, #118, #119, #121, #122, #124)</p><p>aredington said: This appears to still be a problem with the generated bytecode in 1.3.0. Examining the bytecode for last, the problem has moved to invokeStatic:</p>
<p>&lt;pre&gt;<br/>
public static java.lang.Object invokeStatic(java.lang.Object) throws java.lang.Exception;<br/>
Code:<br/>
0: aload_0<br/>
1: invokestatic #50; //Method clojure/core$next.invokeStatic:(Ljava/lang/Object;)Ljava/lang/Object;<br/>
4: dup<br/>
5: ifnull 25<br/>
8: getstatic #56; //Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;<br/>
11: if_acmpeq 26<br/>
14: aload_0<br/>
15: invokestatic #50; //Method clojure/core$next.invokeStatic:(Ljava/lang/Object;)Ljava/lang/Object;<br/>
18: astore_0<br/>
19: goto 0<br/>
22: goto 30<br/>
25: pop<br/>
26: aload_0<br/>
27: invokestatic #59; //Method clojure/core$first.invokeStatic:(Ljava/lang/Object;)Ljava/lang/Object;<br/>
30: areturn<br/>
&lt;/pre&gt;</p>
<p>Line number 22 is an unreachable goto given the prior goto on line 19.</p>ApprovalVettedGlobal Rank[CLJ-445] Method/Constructor resolution does not factor in widening conversion of primitive argshttp://dev.clojure.org/jira/browse/CLJ-445
Clojure<p>Problem:<br/>
When making java calls (or inlined functions), if both args and param are primitive, no widening conversion is used to locate the proper overloaded method/constructor.</p>
<p>Examples:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt; (<span class="code-object">Integer</span>. (<span class="code-object">byte</span> 0))
java.lang.IllegalArgumentException: No matching ctor found <span class="code-keyword">for</span> class java.lang.<span class="code-object">Integer</span> (NO_SOURCE_FILE:0)
&lt;/code&gt;&lt;/pre&gt;
The above occurs because there is no <span class="code-object">Integer</span>(<span class="code-object">byte</span>) constructor, though it should match on <span class="code-object">Integer</span>(<span class="code-object">int</span>).
&lt;pre&gt;&lt;code&gt;user=&gt; (bit-shift-left (<span class="code-object">byte</span> 1) 1)
Reflection warning, NO_SOURCE_PATH:3 - call to shiftLeft can't be resolved.
2</pre>
</div></div>
<p>In the above, a call is made via reflection to Numbers.shiftLeft(Object, Object) and its associated auto-boxing, instead of directly to the perfectly adequate Numbers.shiftLeft(long, int).</p>
<p>Workarounds:<br/>
Explicitly casting to the formal type.</p>
<p>Ancillary benefits of fixing:<br/>
It would also reduce the amount of method overloading, e.g., RT.intCast(char), intCast(byte), intCast(short), could all be removed, since such calls would pass to RT.intCast(int).</p>CLJ-445Method/Constructor resolution does not factor in widening conversion of primitive argsEnhancementMajorIn ProgressUnresolvedUnassignedAlexander TaggartWed, 29 Sep 2010 01:02:00 -0500Fri, 30 Jan 2015 14:16:56 -0600Backlog35<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/445">http://www.assembla.com/spaces/clojure/tickets/445</a><br/>
Attachments:<br/>
fixbug445.diff - <a href="https://www.assembla.com/spaces/clojure/documents/b6gDSUZOur36b9eJe5cbCb/download/b6gDSUZOur36b9eJe5cbCb">https://www.assembla.com/spaces/clojure/documents/b6gDSUZOur36b9eJe5cbCb/download/b6gDSUZOur36b9eJe5cbCb</a></p><p>ataggart said: [<a href="file:b6gDSUZOur36b9eJe5cbCb">file:b6gDSUZOur36b9eJe5cbCb</a>]</p><p>ataggart said: Also fixes #446.</p><p>The patch is causing a test failure </p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">[java] Exception in thread <span class="code-quote">"main"</span> java.lang.IllegalArgumentException:
More than one matching method found: equiv, compiling:(clojure/pprint/cl_format.clj:428)</pre>
</div></div>
<p>Can you take a look?</p><p>The failing test happens when trying to find the correct equiv for signature <tt>(Number, long)</tt>. Is the compiler wrong to propose this signature, or is the resolution method wrong in not having an answer? (It thinks two signatures are tied: <tt>(Object, long)</tt> and <tt>(Number, Number)</tt>.)</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">Exception in thread <span class="code-quote">"main"</span> java.lang.IllegalArgumentException: More than one matching method found: equiv, compiling:(clojure/pprint/cl_format.clj:428)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6062)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6050)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.access$100(<span class="code-object">Compiler</span>.java:35)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5492)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2372)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$InvokeExpr.parse(<span class="code-object">Compiler</span>.java:3277)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6057)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5231)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5527)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5231)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2385)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5231)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5527)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2385)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5231)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5527)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5231)
at clojure.lang.<span class="code-object">Compiler</span>$FnMethod.parse(<span class="code-object">Compiler</span>.java:4667)
at clojure.lang.<span class="code-object">Compiler</span>$FnExpr.parse(<span class="code-object">Compiler</span>.java:3397)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6053)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6043)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.access$100(<span class="code-object">Compiler</span>.java:35)
at clojure.lang.<span class="code-object">Compiler</span>$DefExpr$Parser.parse(<span class="code-object">Compiler</span>.java:480)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5866)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:5827)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6114)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:6545)
at clojure.lang.RT.loadResourceScript(RT.java:340)
at clojure.lang.RT.loadResourceScript(RT.java:331)
at clojure.lang.RT.load(RT.java:409)
at clojure.lang.RT.load(RT.java:381)
at clojure.core$load$fn__1427.invoke(core.clj:5308)
at clojure.core$load.doInvoke(core.clj:5307)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.pprint$eval3969.invoke(pprint.clj:46)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6110)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:6545)
at clojure.lang.RT.loadResourceScript(RT.java:340)
at clojure.lang.RT.loadResourceScript(RT.java:331)
at clojure.lang.RT.load(RT.java:409)
at clojure.lang.RT.load(RT.java:381)
at clojure.core$load$fn__1427.invoke(core.clj:5308)
at clojure.core$load.doInvoke(core.clj:5307)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.core$load_one.invoke(core.clj:5132)
at clojure.core$load_lib.doInvoke(core.clj:5169)
at clojure.lang.RestFn.applyTo(RestFn.java:143)
at sun.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:77)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:28)
at clojure.core$apply.invoke(core.clj:602)
at clojure.core$load_libs.doInvoke(core.clj:5203)
at clojure.lang.RestFn.applyTo(RestFn.java:138)
at sun.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:77)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:28)
at clojure.core$apply.invoke(core.clj:604)
at clojure.core$use.doInvoke(core.clj:5283)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.main$repl.doInvoke(main.clj:196)
at clojure.lang.RestFn.invoke(RestFn.java:422)
at clojure.main$repl_opt.invoke(main.clj:267)
at clojure.main$main.doInvoke(main.clj:362)
at clojure.lang.RestFn.invoke(RestFn.java:409)
at clojure.lang.Var.invoke(Var.java:401)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:518)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: More than one matching method found: equiv
at clojure.lang.Reflector.getMatchingParams(Reflector.java:639)
at clojure.lang.Reflector.getMatchingParams(Reflector.java:578)
at clojure.lang.Reflector.getMatchingMethod(Reflector.java:569)
at clojure.lang.<span class="code-object">Compiler</span>$StaticMethodExpr.&lt;init&gt;(<span class="code-object">Compiler</span>.java:1439)
at clojure.lang.<span class="code-object">Compiler</span>$HostExpr$Parser.parse(<span class="code-object">Compiler</span>.java:896)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6055)
... 115 more</pre>
</div></div><p>In working on implementing support for vararg methods, I found a number of flaws with the previous solutions. Please disregard them.</p>
<p>I've attached a single patch (reflector-compiler-numbers.diff) which is a rather substantial overhaul of the Reflector code, with some enhancements to the Compiler and Numbers code. </p>
<p>The patch notes:</p>
<ul class="alternate" type="square">
<li>Moved reflection functionality from Compiler to Reflector.</li>
<li>Reflector supports finding overloaded methods by widening conversion, boxing conversion, and casting.</li>
<li>During compilation Reflector attempts to find best wildcard match.</li>
<li>Reflector refers to <tt>&#42;unchecked-math*</tt> when reflectively invoking methods and constructors.</li>
<li>Both Reflector and Compiler support variable arity java methods and constructor; backwards compatible with passing an array or nil in the vararg slot.</li>
<li>Added more informative error messages to Reflector.</li>
<li>Added tests to clojure.test-clojure.reflector.</li>
<li>Altered overloaded functions of clojure.lang.Numbers to service Object/double/long params; fixes some ambiguity issues and avoids unnecessary boxing in some cases.</li>
<li>Patch closes issues 380, 440, 445, 666, and possibly 259 (not enough detail provided).</li>
</ul>
<p>Updated patch to fix a bug where a concrete class with multiple identical Methods (e.g., one from an interface, another from an abstract class) would result in ambiguity. Now resolved by arbitrary selection (this is what the original code did as well albeit not explicitly).</p><p>Updated patch to work with latest master branch.</p><p>patch appears to be missing test file clojure/test_clojure/reflector.clj.</p><p>Bit by git.</p>
<p>Patch corrected to contain clojure.test-clojure.reflector.</p><p>Rich: I verified that the patch applied but reviewed only briefly, knowing you will want to go over this one closely.</p><p>After applying this patch, I am getting method missing errors:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">java.lang.NoSuchMethodError: clojure.lang.Numbers.lt(JLjava/lang/<span class="code-object">Object</span>;)</pre>
</div></div>
<p>but only when using compiled code, e.g. the same code works in the REPL and then fails after compilation. Haven't been able to isolate an example that I can share here yet, but hoping this will cause someone to have an "a, hah" moment...</p><p>The patch now contains only the minimal changes needed to support widening conversion. Cleanup of Numbers overloads, etc., can wait until this patch gets applied. The vararg support is in a separate patch on <a href="http://dev.clojure.org/jira/browse/CLJ-440" title="java method calls cannot omit varargs">CLJ-440</a>.</p><p>Please test patch</p><p>FYI: Patch applies cleanly on master and all tests pass as of 4/21 (2011)</p><p>This work is too big to take into the 1.3 beta right now. We'll revisit for a future release.</p><p>To better facilitate understanding of the changes, I've broken them up into two patches, each with a number of isolable, incremental commits:</p>
<p><b>reorg-reflector.patch:</b> Moves the reflection/invocation code from Compiler to Reflector, and eliminates redundant code. The result is a single code base for resolving methods/constructors, which will allow for altering that mechanism without excess external coordination. <em>This contains no behaviour changes.</em></p>
<p><b>prim-conversion.patch:</b> Depends on the above. Alters the method/constructor resolution process:</p>
<ul>
<li>more consistent with java resolution, especially when calling pre-1.5 APIs</li>
<li>adds support for widening conversion of primitive numerics of the same category (this is more strict than java, and more clojuresque)</li>
<li>adds support for wildcard matches at compile-time (i.e., you don't need to type-hint every arg to avoid reflection).</li>
</ul>
<p>This also provides a base to add further features, e.g., <a href="http://dev.clojure.org/jira/browse/CLJ-666" title="Add support for Big* numeric types to Reflector">CLJ-666</a>.</p><p>It's documented in situ, but here are the conversion rules that the reflector uses to find methods:</p>
<ol>
<li>By Type:
<ul>
<li>object to ancestor type</li>
<li>primitive to a wider primitive of the same numeric category (new)</li>
</ul>
</li>
<li>Boxing:
<ul>
<li>boxed number to its primitive</li>
<li>boxed number to a wider primitive of the same numeric category (new for Short and Byte args)</li>
<li>primitive to its boxed value</li>
<li>primitive to Number or Object (new)</li>
</ul>
</li>
<li>Casting:
<ul>
<li>long to int</li>
<li>double to float</li>
</ul>
</li>
</ol>
<p>prim-conversion-update-1.patch applies to current master.</p><p>Created <a href="http://dev.clojure.org/jira/browse/CLJ-792" title="Refactor method resolution code out of Compiler and into Reflector">CLJ-792</a> for the reflector reorg.</p><p>prim-conversion-update-1.patch does not apply as of f5bcf64. </p>
<p>Is <a href="http://dev.clojure.org/jira/browse/CLJ-792" title="Refactor method resolution code out of Compiler and into Reflector">CLJ-792</a> now a prerequisite of this ticket?</p><p>Yes, after the original patch was deemed "too big".</p>
<p>After this much time with no action from TPTB, feel free to kill both tickets.</p><p>Again, not sure if this is any help, but I've tested starting from Clojure head as of Feb 20, 2012, applying clj-792-reorg-reflector-patch2.txt attached to <a href="http://dev.clojure.org/jira/browse/CLJ-792" title="Refactor method resolution code out of Compiler and into Reflector">CLJ-792</a>, and then applying clj-445-prim-conversion-update-2-patch.txt attached to this ticket, and the result compiles and passes all but 2 tests. I don't know whether those failures are easy to fix or not, or whether issues may have been introduced with these patches.</p><p>As of 1.7.0-alpha5 the two examples in the ticket description run without exceptions/reflection-warnings</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt; (set! *warn-on-reflection* <span class="code-keyword">true</span>)
<span class="code-keyword">true</span>
user=&gt; (bit-shift-left (<span class="code-object">byte</span> 1) 1)
2
user=&gt; (<span class="code-object">Integer</span>. (<span class="code-object">byte</span> 0))
0</pre>
</div></div>ApprovalVettedGlobal Rank[CLJ-415] smarter assert (prints locals)http://dev.clojure.org/jira/browse/CLJ-415
Clojure<p>Here is an implementation you can paste into a repl. Feedback wanted:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(defn ^{:<span class="code-keyword">private</span> <span class="code-keyword">true</span>} local-bindings
<span class="code-quote">"Produces a map of the names of local bindings to their values."</span>
[env]
(let [symbols (map key env)]
(zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))
(defmacro <span class="code-keyword">assert</span>
"Evaluates expr and <span class="code-keyword">throws</span> an exception <span class="code-keyword">if</span> it does not evaluate to
logical <span class="code-keyword">true</span>."
{:added <span class="code-quote">"1.0"</span>}
[x]
(when *<span class="code-keyword">assert</span>*
(let [bindings (local-bindings &amp;env)]
`(when-not ~x
(let [sep# (<span class="code-object">System</span>/getProperty <span class="code-quote">"line.separator"</span>)]
(<span class="code-keyword">throw</span> (AssertionError. (apply str <span class="code-quote">"Assert failed: "</span> (pr-str '~x) sep#
(map (fn [[k# v#]] (str <span class="code-quote">"\t"</span> k# <span class="code-quote">" : "</span> v# sep#)) ~bindings)))))))))</pre>
</div></div>CLJ-415smarter assert (prints locals)EnhancementMajorOpenUnresolvedUnassignedAssembla ImportererrormsgsThu, 29 Jul 2010 17:45:00 -0500Mon, 6 Jul 2015 16:02:17 -0500Backlog23<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/415">http://www.assembla.com/spaces/clojure/tickets/415</a></p><p>alexdmiller said: A simple example I tried for illustration:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt; (let [a 1 b 2] (<span class="code-keyword">assert</span> (= a b)))
#&lt;CompilerException java.lang.AssertionError: Assert failed: (= a b)
a : 1
b : 2</pre>
</div></div><p>fogus said: Of course it's weird if you do something like:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(let [x 1 y 2 z 3 a 1 b 2 c 3] (<span class="code-keyword">assert</span> (= x y)))
java.lang.AssertionError: Assert failed: (= x y)
x : 1
y : 2
z : 3
a : 1
b : 2
c : 3
(NO_SOURCE_FILE:0)
&lt;/code&gt;&lt;/pre&gt;
So maybe it could be slightly changed to:
&lt;pre&gt;&lt;code&gt;(defmacro <span class="code-keyword">assert</span>
<span class="code-quote">"Evaluates expr and <span class="code-keyword">throws</span> an exception <span class="code-keyword">if</span> it does not evaluate to logical <span class="code-keyword">true</span>."</span>
{:added <span class="code-quote">"1.0"</span>}
[x]
(when *<span class="code-keyword">assert</span>*
(let [bindings (local-bindings &amp;env)]
`(when-not ~x
(let [sep# (<span class="code-object">System</span>/getProperty <span class="code-quote">"line.separator"</span>)
form# '~x]
(<span class="code-keyword">throw</span> (AssertionError. (apply str <span class="code-quote">"Assert failed: "</span> (pr-str form#) sep#
(map (fn [[k# v#]]
(when (some #{k#} form#)
(str <span class="code-quote">"\t"</span> k# <span class="code-quote">" : "</span> v# sep#)))
~bindings)))))))))
&lt;/code&gt;&lt;/pre&gt;
So that. now it's just:
&lt;pre&gt;&lt;code&gt;(let [x 1 y 2 z 3 a 1 b 2 c 3] (<span class="code-keyword">assert</span> (= x y)))
java.lang.AssertionError: Assert failed: (= x y)
x : 1
y : 2
(NO_SOURCE_FILE:0)</pre>
</div></div>
<p>:f</p><p>fogus said: Hmmm, but that fails entirely for: (let <span class="error">&#91;x 1 y 2 z 3 a 1 b 2 c 3&#93;</span> (assert (= <span class="error">&#91;x y&#93;</span> <span class="error">&#91;a c&#93;</span>))). So maybe it's better just to print all of the locals unless you really want to get complicated.<br/>
:f</p><p>jawolfe said: See also some comments in:</p>
<p><a href="http://groups.google.com/group/clojure-dev/browse_frm/thread/68d49cd7eb4a4899/9afc6be4d3f8ae27?lnk=gst&amp;q=assert#9afc6be4d3f8ae27">http://groups.google.com/group/clojure-dev/browse_frm/thread/68d49cd7eb4a4899/9afc6be4d3f8ae27?lnk=gst&amp;q=assert#9afc6be4d3f8ae27</a></p>
<p>Plus one more suggestion to add to the mix: in addition to / instead of printing the locals, how about saving them somewhere. For example, the var <b>assert-bindings</b> could be bound to the map of locals. This way you don't run afoul of infinite/very large sequences, and allow the user to do more detailed interrogation of the bad values (especially useful when some of the locals print opaquely).</p><p>stuart.sierra said: Another approach, which I wil willingly donate:<br/>
<a href="http://github.com/stuartsierra/lazytest/blob/master/src/main/clojure/lazytest/expect.clj">http://github.com/stuartsierra/lazytest/blob/master/src/main/clojure/lazytest/expect.clj</a></p><p>There's one more tweak to fogus's last comment, which I'm actually using. You need to flatten the quoted form before you can use 'some' to check whether the local was used in the form:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(defmacro <span class="code-keyword">assert</span>
<span class="code-quote">"Evaluates expr and <span class="code-keyword">throws</span> an exception <span class="code-keyword">if</span> it does not evaluate to logical <span class="code-keyword">true</span>."</span>
{:added <span class="code-quote">"1.0"</span>}
[x]
(when *<span class="code-keyword">assert</span>*
(let [bindings (local-bindings &amp;env)]
`(when-not ~x
(let [sep# (<span class="code-object">System</span>/getProperty <span class="code-quote">"line.separator"</span>)
form# '~x]
(<span class="code-keyword">throw</span> (AssertionError. (apply str <span class="code-quote">"Assert failed: "</span> (pr-str form#) sep#
(map (fn [[k# v#]]
(when (some #{k#} (flatten form#))
(str <span class="code-quote">"\t"</span> k# <span class="code-quote">" : "</span> v# sep#)))
~bindings)))))))))</pre>
</div></div><p>I am holding off on this until we have more solidity around <a href="http://dev.clojure.org/display/design/Error+Handling">http://dev.clojure.org/display/design/Error+Handling</a>. (Considering, for instance, having <b>all</b> exceptions thrown from Clojure provide access to locals.)</p>
<p>When my pipe dream fades I will come back and screen this before the next release.</p><p>Why try to guess what someone wants to do with the locals (or any other context, for that matter) when you can specify a callback (see below). This would have been useful last week when I had an assertion that failed only on the CI box, where no debugger is available.</p>
<p>Rich, at the risk of beating a dead horse, I still think this is a good idea. Debuggers are not always available, and this is an example of where a Lisp is intrinsically capable of providing better information than can be had in other environments. If you want a patch for the code below please mark waiting on me, otherwise please decline this ticket so I stop looking at it. <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(def ^:dynamic *<span class="code-keyword">assert</span>-handler* nil)
(defn ^{:<span class="code-keyword">private</span> <span class="code-keyword">true</span>} local-bindings
<span class="code-quote">"Produces a map of the names of local bindings to their values."</span>
[env]
(let [symbols (map key env)]
(zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))
(defmacro <span class="code-keyword">assert</span>
[x]
(when *<span class="code-keyword">assert</span>*
(let [bindings (local-bindings &amp;env)]
`(when-not ~x
(let [sep# (<span class="code-object">System</span>/getProperty <span class="code-quote">"line.separator"</span>)
form# '~x]
(<span class="code-keyword">if</span> *<span class="code-keyword">assert</span>-handler*
(*<span class="code-keyword">assert</span>-handler* form# ~bindings)
(<span class="code-keyword">throw</span> (AssertionError. (apply str <span class="code-quote">"Assert failed: "</span> (pr-str form#) sep#
(map (fn [[k# v#]]
(when (some #{k#} (flatten form#))
(str <span class="code-quote">"\t"</span> k# <span class="code-quote">" : "</span> v# sep#)))
~bindings))))))))))</pre>
</div></div><p>A slight improvement I made in my own version of this code: flatten does not affect set literals. So if you do (assert (some #{x} <span class="error">&#91;a b c d&#93;</span>)) the value of x will not be printed. Here's a modified flatten that does the job:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-none">(defn symbols [sexp]
"Returns just the symbols from the expression, including those
inside literals (sets, maps, lists, vectors)."
(distinct (filter symbol? (tree-seq coll? seq sexp))))</pre>
</div></div><p>Attaching git format patch clj-415-assert-prints-locals-v1.txt of Stuart Halloway's version of this idea. I'm not advocating it over the other variations, just getting a file attached to the JIRA ticket.</p><p>Previous patch was incompatible with <a href="http://dev.clojure.org/jira/browse/CLJ-1005" title="Use transient map in zipmap">CLJ-1005</a>, which moves zipmap later in clojure.core. Rewrote to use into.</p><p>Both patches are somehow incompatible with <a href="http://dev.clojure.org/jira/browse/CLJ-1224" title="Records do not cache hash like normal maps">CLJ-1224</a>. When building my compojure-api project I get</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">Exception in thread <span class="code-quote">"main"</span> java.lang.UnsupportedOperationException: Can't type hint a primitive local, compiling:(schema/core.clj:680:27)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6569)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$MapExpr.parse(<span class="code-object">Compiler</span>.java:3050)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6558)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$InvokeExpr.parse(<span class="code-object">Compiler</span>.java:3791)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6751)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$NewExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2614)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$ThrowExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2409)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5889)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2785)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5889)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:6205)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2777)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2785)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2785)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5891)
at clojure.lang.<span class="code-object">Compiler</span>$FnMethod.parse(<span class="code-object">Compiler</span>.java:5322)
at clojure.lang.<span class="code-object">Compiler</span>$FnExpr.parse(<span class="code-object">Compiler</span>.java:3925)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6747)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5891)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:6205)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$IfExpr$Parser.parse(<span class="code-object">Compiler</span>.java:2777)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5891)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:6205)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6737)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5891)
at clojure.lang.<span class="code-object">Compiler</span>$NewInstanceMethod.parse(<span class="code-object">Compiler</span>.java:8165)
at clojure.lang.<span class="code-object">Compiler</span>$NewInstanceExpr.build(<span class="code-object">Compiler</span>.java:7672)
at clojure.lang.<span class="code-object">Compiler</span>$NewInstanceExpr$DeftypeParser.parse(<span class="code-object">Compiler</span>.java:7549)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5889)
at clojure.lang.<span class="code-object">Compiler</span>$LetExpr$Parser.parse(<span class="code-object">Compiler</span>.java:6205)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6749)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6511)
at clojure.lang.<span class="code-object">Compiler</span>$BodyExpr$Parser.parse(<span class="code-object">Compiler</span>.java:5891)
at clojure.lang.<span class="code-object">Compiler</span>$FnMethod.parse(<span class="code-object">Compiler</span>.java:5322)
at clojure.lang.<span class="code-object">Compiler</span>$FnExpr.parse(<span class="code-object">Compiler</span>.java:3925)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSeq(<span class="code-object">Compiler</span>.java:6747)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6550)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6805)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:436)
at plumbing.fnk.schema$eval12507$loading__5352__auto____12508.invoke(schema.clj:1)
at plumbing.fnk.schema$eval12507.invoke(schema.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:457)
at plumbing.core$eval12244$loading__5352__auto____12245.invoke(core.clj:1)
at plumbing.core$eval12244.invoke(core.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:930)
at compojure.api.meta$eval11960$loading__5352__auto____11961.invoke(meta.clj:1)
at compojure.api.meta$eval11960.invoke(meta.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:703)
at compojure.api.core$eval11954$loading__5352__auto____11955.invoke(core.clj:1)
at compojure.api.core$eval11954.invoke(core.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:457)
at compojure.api.sweet$eval11948$loading__5352__auto____11949.invoke(sweet.clj:1)
at compojure.api.sweet$eval11948.invoke(sweet.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:703)
at com.climate.scouting.resources$eval10202$loading__5352__auto____10203.invoke(resources.clj:1)
at com.climate.scouting.resources$eval10202.invoke(resources.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5761)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:703)
at com.climate.scouting.service$eval8256$loading__5352__auto____8257.invoke(service.clj:1)
at com.climate.scouting.service$eval8256.invoke(service.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5679)
at clojure.core$load_lib$fn__5409.invoke(core.clj:5719)
at clojure.core$load_lib.doInvoke(core.clj:5718)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$load_libs.doInvoke(core.clj:5757)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:632)
at clojure.core$require.doInvoke(core.clj:5840)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at com.climate.scouting.dev_server$eval8250$loading__5352__auto____8251.invoke(dev_server.clj:1)
at com.climate.scouting.dev_server$eval8250.invoke(dev_server.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6797)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.RT.loadResourceScript(RT.java:371)
at clojure.lang.RT.loadResourceScript(RT.java:362)
at clojure.lang.RT.load(RT.java:446)
at clojure.lang.RT.load(RT.java:412)
at clojure.core$load$fn__5460.invoke(core.clj:5874)
at clojure.core$load.doInvoke(core.clj:5873)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval58$fn__69.invoke(form-init9085313321330645488.clj:1)
at user$eval58.invoke(form-init9085313321330645488.clj:1)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6808)
at clojure.lang.<span class="code-object">Compiler</span>.eval(<span class="code-object">Compiler</span>.java:6798)
at clojure.lang.<span class="code-object">Compiler</span>.load(<span class="code-object">Compiler</span>.java:7253)
at clojure.lang.<span class="code-object">Compiler</span>.loadFile(<span class="code-object">Compiler</span>.java:7191)
at clojure.main$load_script.invoke(main.clj:275)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invoke(main.clj:308)
at clojure.main$null_opt.invoke(main.clj:343)
at clojure.main$main.doInvoke(main.clj:421)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.UnsupportedOperationException: Can't type hint a primitive local
at clojure.lang.<span class="code-object">Compiler</span>$LocalBindingExpr.&lt;init&gt;(<span class="code-object">Compiler</span>.java:5792)
at clojure.lang.<span class="code-object">Compiler</span>.analyzeSymbol(<span class="code-object">Compiler</span>.java:6929)
at clojure.lang.<span class="code-object">Compiler</span>.analyze(<span class="code-object">Compiler</span>.java:6532)
... 299 more
Failed.</pre>
</div></div><p>Michael, are you finding these incompatibilities between patches because you want to run a modified version of Clojure with all of these patches? Understood, if so.</p>
<p>If you are looking for pairs of patches that are incompatible with each other, I'd recommend a different hobby <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/> They get applied at the rate of about 9 per month, on average, so there should be plenty of time to resolve inconsistencies between them later.</p>ApprovalVettedGlobal RankPatchCodeWaiting Onrichhickey[CLJ-274] cannot close over mutable fields (in deftype)http://dev.clojure.org/jira/browse/CLJ-274
Clojure<p>Simplest case:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt;
(deftype Bench [#^{:unsynchronized-mutable <span class="code-keyword">true</span>} val]
<span class="code-object">Runnable</span>
(run [_]
(fn [] (set! val 5))))
java.lang.IllegalArgumentException: Cannot assign to non-mutable: val (NO_SOURCE_FILE:5)</pre>
</div></div>
<p>Functions should be able to mutate mutable fields in their surrounding deftype (just like inner classes do in Java).</p>
<p>Filed as bug, because the loop special form expands into a fn form sometimes:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt;
(deftype Bench [#^{:unsynchronized-mutable <span class="code-keyword">true</span>} val]
<span class="code-object">Runnable</span>
(run [_]
(let [x (loop [] (set! val 5))])))
java.lang.IllegalArgumentException: Cannot assign to non-mutable: val (NO_SOURCE_FILE:9)</pre>
</div></div>CLJ-274cannot close over mutable fields (in deftype)DefectMajorOpenUnresolvedUnassignedNonedeftypeTue, 23 Feb 2010 16:41:00 -0600Sun, 19 Jul 2015 10:34:39 -0500Backlog24<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/274">http://www.assembla.com/spaces/clojure/tickets/274</a></p><p>donmullen said: Updated each run to <span class="error">&#91;_&#93;</span> for new syntax.</p>
<p>Now gives exception listed.</p><p>richhickey said: We're not going to allow closing over mutable fields. Instead we'll have to generate something other than fn for loops et al used as expressions. Not going to come before cinc</p><p>The patch for <a href="http://dev.clojure.org/jira/browse/CLJ-1226" title="set! of a deftype field using field-access syntax causes ClassCastException">CLJ-1226</a> makes this work:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(deftype Bench [#^{:unsynchronized-mutable <span class="code-keyword">true</span>} val]
<span class="code-object">Runnable</span>
(run [_]
(let [x (loop [] (set! (.val _) 5))])))</pre>
</div></div>
<p>If there's interest, I could provide a patch that converts closed over mutable field access by generated fns (for loop/try) into field access on closed over "this", i.e. val -&gt; (.val this)</p><p>Related tickets: <a href="http://dev.clojure.org/jira/browse/CLJ-1075" title="deftype: compiler error on set! for mutable field"><del>CLJ-1075</del></a> <a href="http://dev.clojure.org/jira/browse/CLJ-1023" title="non-tail-position try block breaks mutable fields in deftype"><del>CLJ-1023</del></a></p>ApprovalVettedGlobal Rank[CLJ-348] reify allows use of qualified name as method parameterhttp://dev.clojure.org/jira/browse/CLJ-348
Clojure<p>This should complain about using a fully-qualified name as a parameter:</p>
<p>(defmacro lookup []<br/>
`(reify clojure.lang.ILookup<br/>
(valAt <span class="error">&#91;_ key&#93;</span>)))</p>
<p>Instead it simply ignores that parameter in the method body in favour of clojure.core/key.</p>CLJ-348reify allows use of qualified name as method parameterDefectMinorOpenUnresolvedUnassignedAssembla ImporterThu, 13 May 2010 14:30:00 -0500Fri, 26 Jul 2013 10:31:07 -0500Backlog02<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/348">http://www.assembla.com/spaces/clojure/tickets/348</a><br/>
Attachments:<br/>
0001-Add-a-test-for-348-reify-shouldn-t-accept-qualified-.patch - <a href="https://www.assembla.com/spaces/clojure/documents/d2xUJIxTyr36fseJe5cbLA/download/d2xUJIxTyr36fseJe5cbLA">https://www.assembla.com/spaces/clojure/documents/d2xUJIxTyr36fseJe5cbLA/download/d2xUJIxTyr36fseJe5cbLA</a></p><p>technomancy said: [<a href="file:d2xUJIxTyr36fseJe5cbLA">file:d2xUJIxTyr36fseJe5cbLA</a>]: A test to expose the unwanted behaviour</p><p>richhickey said: I'm not sure the bug is what you say it is, or the resolution should be what you suggest. The true problem is the resolution of key when qualified. Another possibility is to ignore the qualifier there.</p><p>technomancy said: Interesting. So it's not appropriate to require auto-gensym here? Why are the rules different for reify methods vs proxy methods?</p>
<p>&gt; (defmacro lookup []<br/>
`(proxy <span class="error">&#91;clojure.lang.ILookup&#93;</span> []<br/>
(valAt <span class="error">&#91;key&#93;</span> key)))<br/>
&gt; (lookup)</p>
<p>Can't use qualified name as parameter: clojure.core/key<br/>
<span class="error">&#91;Thrown class java.lang.Exception&#93;</span></p>ApprovalVettedGlobal Rank[CLJ-326] add :as-of option to referhttp://dev.clojure.org/jira/browse/CLJ-326
Clojure<p>Discussed here: <a href="http://groups.google.com/group/clojure-dev/msg/74af612909dcbe56">http://groups.google.com/group/clojure-dev/msg/74af612909dcbe56</a></p>
<p>:as-of allows library authors to specify a known subset of vars to refer from clojure (or <b>any other library</b> which would use :added metadata).</p>
<p>(ns foo (:refer-clojure :as-of "1.1")) is equivalent to (ns foo (:refer-clojure :only <span class="error">&#91;public-documented-vars-which-already-existed-in-1.1&#93;</span>))</p>CLJ-326add :as-of option to referEnhancementMinorIn ProgressUnresolvedChristophe GrandChristophe GrandFri, 30 Apr 2010 02:49:00 -0500Fri, 26 Jul 2013 15:25:59 -0500Backlog00<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/326">http://www.assembla.com/spaces/clojure/tickets/326</a><br/>
Attachments:<br/>
add-as-of-to-refer.patch - <a href="https://www.assembla.com/spaces/clojure/documents/a8SumUvcOr37SmeJe5cbLA/download/a8SumUvcOr37SmeJe5cbLA">https://www.assembla.com/spaces/clojure/documents/a8SumUvcOr37SmeJe5cbLA/download/a8SumUvcOr37SmeJe5cbLA</a></p><p>cgrand said: [<a href="file:a8SumUvcOr37SmeJe5cbLA">file:a8SumUvcOr37SmeJe5cbLA</a>]: requires application of #325</p><p>richhickey said: Do we still need this?</p>ApprovalVettedGlobal Rank[CLJ-1104] Concurrent with-redefs do not unwind properly, leaving a var permanently changedhttp://dev.clojure.org/jira/browse/CLJ-1104
Clojure<p>On 1.4 and latest master:</p>
<p>user&gt; (defn ten [] 10)<br/>
#'user/ten<br/>
user&gt; (doall (pmap #(with-redefs [ten (fn [] %)] (ten)) (range 20 100)))<br/>
(20 21 22 23 24 25 34 27 28 29 30 31 32 33 34 35 36 37 38 39 39 35 42 43 44 45 48 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 79 87 88 89 90 91 92 93 94 95 97 92 98 99)<br/>
user&gt; (ten)<br/>
79</p>
<p>Not sure if this is a bug per se, but the doc doesn't mention lack of concurrency safety and my expectation was that the original value would always be restored after any (arbitrarily interleaved) sequence of with-redefs calls. </p>Mac OS, Java 6CLJ-1104Concurrent with-redefs do not unwind properly, leaving a var permanently changedEnhancementMinorOpenUnresolvedUnassignedJason WolfeWed, 7 Nov 2012 17:46:19 -0600Fri, 26 Jul 2013 10:30:06 -0500Release 1.4Release 1.5Backlog00<p>The with-redefs doc (v1.4.0) says "These temporary changes will be visible in all threads." That sounds non-thread-safe to me.</p>
<p>In general, changes to var root bindings are not thread safe.</p><p>As I understand it, with-redefs is mainly used in test suites to mock out vars. It was introduced when vars became static by default and a lot of testsuites had been using binding for mocking. Maybe the docstring should be amended with something along the lines of: When using this you have to ensure that only a single thread is interacting with redef'd vars.</p><p>Behavior find as is, doc string change would be fine.</p><p>Patch clj-1104-doc-unsafety-of-concurrent-with-redefs-v1.txt dated Nov 25 2012 updates doc string of with-redefs to make it clear that concurrent use is unsafe.</p>ApprovalVettedGlobal RankPatchCode[CLJ-84] GC Issue 81: compile gen-class fail when class returns selfhttp://dev.clojure.org/jira/browse/CLJ-84
Clojure<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">Reported by davidhaub, Feb 14, 2009
When attempting to compile the following program, clojure fails with a
ClassNotFoundException. It occurs because one of the methods returns the
same class that is being generated. If the returnMe method below is
changed to <span class="code-keyword">return</span> an <span class="code-object">Object</span>, the compile succeeds.
Beware when testing! If the classpath contains a class file (say from a
prior successful build when the returnMe method was changed to <span class="code-keyword">return</span> an
object), the compile will succeed. Always clear out the
clojure.compile.path prior to compiling.
;badgenclass.clj
(ns badgenclass
(:gen-class
:state state
:methods
[[returnMe [] badgenclass]]
:init init))
(defn -init []
[[] nil])
(defn -returnMe [<span class="code-keyword">this</span>]
<span class="code-keyword">this</span>)
#!/bin/sh
rm -rf classes
mkdir classes
java -cp lib/clojure.jar:classes:. -Dclojure.compile.path=classes \
clojure.lang.Compile badgenclass
Comment 1 by chouser, Mar 07, 2009
Attached is a patch that accepts strings or symbols <span class="code-keyword">for</span> parameter and <span class="code-keyword">return</span> class
names, and generates the appropriate bytecode without calling <span class="code-object">Class</span>/forName. It
fixes <span class="code-keyword">this</span> issue, but because 'ns' doesn't resolve :gen-class's arguments, class
names aren't checked as early anymore. :gen-class-created classes with invalid
parameter or <span class="code-keyword">return</span> types can even be instantiated, and no error will be reported
until the broken method is called.
One possible alternative would be to call <span class="code-object">Class</span>/forName on any symbols given, but
allow strings to use the method given by <span class="code-keyword">this</span> patch. To <span class="code-keyword">return</span> your own type, you'd
need a method defined like:
[returnMe [] <span class="code-quote">"badgenclass"</span>]
Any thoughts?</pre>
</div></div>CLJ-84GC Issue 81: compile gen-class fail when class returns selfDefectMinorIn ProgressUnresolvedRich HickeyAssembla ImporterWed, 17 Jun 2009 22:30:00 -0500Sat, 27 Jul 2013 10:25:22 -0500BacklogBacklog00<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/84">http://www.assembla.com/spaces/clojure/tickets/84</a><br/>
Attachments:<br/>
genclass-allow-unresolved-classname.patch - <a href="https://www.assembla.com/spaces/clojure/documents/cWS6Aww30r3RbzeJe5afGb/download/cWS6Aww30r3RbzeJe5afGb">https://www.assembla.com/spaces/clojure/documents/cWS6Aww30r3RbzeJe5afGb/download/cWS6Aww30r3RbzeJe5afGb</a></p><p>oranenj said: [<a href="file:cWS6Aww30r3RbzeJe5afGb">file:cWS6Aww30r3RbzeJe5afGb</a>]: on comment 1</p><p>richhickey said: Updating tickets (#8, #19, #30, #31, #126, #17, #42, #47, #50, #61, #64, #69, #71, #77, #79, #84, #87, #89, #96, #99, #103, #107, #112, #113, #114, #115, #118, #119, #121, #122, #124)</p><p>The approach take in the initial patch (delaying resolution of symbols into classes) is fine: gen-class makes no promise about when this happens, and the dynamic approach feels more consistent with Clojure. I think the proposed (but not implemented) use of string/symbol to control when class names are resolved is a bad idea: magical and not implied by the types.</p>
<p>Needed:</p>
<ul>
<li>update the patch to apply cleanly</li>
<li>consider whether totype could live in clojure.reflect.java. (Beware load order dependencies.)</li>
</ul>
<p>Wow, 18-month-old patch, back to haunt me for Halloway'een</p>
<p>So what does it mean that the assignee is Rich, but it's waiting on me?</p><p>I am using Approval = Incomplete plus Waiting On = Someone to let submitters know that there is feedback waiting, and that they can move the ticket forward by acting on it. The distinction is opportunity to contribute (something has been provided to let you move forward) vs. expectation of contribution. </p>ApprovalVettedGlobal RankWaiting Onchouser@n01se.net[CLJ-69] GC Issue 66: Add "keyset" to Clojure; make .keySet for APersistentMap return IPersistentSethttp://dev.clojure.org/jira/browse/CLJ-69
Clojure<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">Reported by wolfe.a.jason, Feb 04, 2009
Describe the feature/change.
Add <span class="code-quote">"keyset"</span> to Clojure; make .keySet <span class="code-keyword">for</span> APersistentMap <span class="code-keyword">return</span> an
IPersistentSet
Was <span class="code-keyword">this</span> discussed on the group? If so, please provide a link to the
discussion:
http:<span class="code-comment">//groups.google.com/group/clojure/browse_thread/thread/66e708e477ae992f/ff3d8d588068b60e?hl=en#ff3d8d588068b60e
</span>
-----------------------------------------------------
A patch is attached. Some notes:
I chose to add a <span class="code-quote">"keyset"</span> function, rather than change the existing <span class="code-quote">"keys"</span>,
so as to avoid breaking anything.
The corresponding RT.keyset function just calls .keySet on the argument.
I would have liked to have <span class="code-quote">"keyset"</span> <span class="code-keyword">return</span> an IPersistentSet even when
passed a (non-Clojure) java.util.Map, but <span class="code-keyword">this</span> seems impossible to <span class="code-keyword">do</span> in
sublinear time because of essentially the same limitation mentioned in the
above thread (the Map <span class="code-keyword">interface</span> does not support getKey() or entryAt()) --
assuming, again, that <span class="code-quote">"get"</span> is supposed to <span class="code-keyword">return</span> the actual (identical?)
key in a set, and not just an .equal key.
I then changed the implementation of .keySet <span class="code-keyword">for</span> APersistentMap to
essentially copy APersistentSet. A more concise alternative would have
been to extend APersistentSet and override the .get method, but that made
me a bit nervous (since <span class="code-keyword">if</span> APeristentSet changed <span class="code-keyword">this</span> could <span class="code-keyword">break</span>).
Anyway, <span class="code-keyword">this</span> is my first patch <span class="code-keyword">for</span> the Java side of Clojure, and I'm not
yet solid on the conventions and aesthetics, so
comments/questions/criticisms/requests <span class="code-keyword">for</span> revisions are very welcome.</pre>
</div></div>CLJ-69GC Issue 66: Add "keyset" to Clojure; make .keySet for APersistentMap return IPersistentSetEnhancementMinorIn ProgressUnresolvedUnassignedAssembla ImporterWed, 17 Jun 2009 00:55:00 -0500Sat, 27 Jul 2013 10:25:22 -0500BacklogBacklog00<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/69">http://www.assembla.com/spaces/clojure/tickets/69</a><br/>
Attachments:<br/>
keyset.patch - <a href="https://www.assembla.com/spaces/clojure/documents/dKgE6mw3Gr3O2PeJe5afGb/download/dKgE6mw3Gr3O2PeJe5afGb">https://www.assembla.com/spaces/clojure/documents/dKgE6mw3Gr3O2PeJe5afGb/download/dKgE6mw3Gr3O2PeJe5afGb</a></p><p>oranenj said: [<a href="file:dKgE6mw3Gr3O2PeJe5afGb">file:dKgE6mw3Gr3O2PeJe5afGb</a>]</p><p>richhickey said: Updating tickets (#8, #19, #30, #31, #126, #17, #42, #47, #50, #61, #64, #69, #71, #77, #79, #84, #87, #89, #96, #99, #103, #107, #112, #113, #114, #115, #118, #119, #121, #122, #124)</p><p>patch not in correct format</p>ApprovalVettedGlobal Rank[CLJ-211] Support arbitrary functional destructuring via -> and ->>http://dev.clojure.org/jira/browse/CLJ-211
Clojure<p>Support arbitrary functional destructuring, that is the use of<br/>
any function in any destructuring form to help unpack data in<br/>
arbitrary ways.</p>
<p>The discussion began here:<br/>
<a href="http://clojure-log.n01se.net/date/2009-11-17.html#09:31c">http://clojure-log.n01se.net/date/2009-11-17.html#09:31c</a></p>
<p>The attached patch implements the spec described here:<br/>
<a href="http://clojure-log.n01se.net/date/2009-11-17.html#10:50a">http://clojure-log.n01se.net/date/2009-11-17.html#10:50a</a></p>
<p>That is, the following examples would now work:</p>
<p>user=&gt; (let <span class="error">&#91;(-&gt; str a) 1&#93;</span> a)<br/>
"1"</p>
<p>user=&gt; (let [<span class="error">&#91;a (-&gt; str b) c&#93;</span> <span class="error">&#91;1 2&#93;</span>] (list a b c))<br/>
(1 "2" nil)</p>
<p>user=&gt; (let [(-&gt;&gt; (map int) <span class="error">&#91;a b&#93;</span>) "ab"] (list a b))<br/>
(97 98)</p>CLJ-211Support arbitrary functional destructuring via -> and ->>EnhancementMinorIn ProgressUnresolvedUnassignedAssembla ImporterTue, 17 Nov 2009 10:43:00 -0600Sat, 27 Jul 2013 10:25:22 -0500Backlog00<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/211">http://www.assembla.com/spaces/clojure/tickets/211</a><br/>
Attachments:<br/>
destructuring-fns.diff - <a href="https://www.assembla.com/spaces/clojure/documents/aHWQ_W06Kr3O89eJe5afGb/download/aHWQ_W06Kr3O89eJe5afGb">https://www.assembla.com/spaces/clojure/documents/aHWQ_W06Kr3O89eJe5afGb/download/aHWQ_W06Kr3O89eJe5afGb</a></p><p>chouser@n01se.net said: [<a href="file:aHWQ_W06Kr3O89eJe5afGb">file:aHWQ_W06Kr3O89eJe5afGb</a>]: <span class="error">&#91;PATCH&#93;</span> Support -&gt; and -&gt;&gt; in destructuring forms.</p><p>cgrand said: I think the current patch suffers from the problem described here <a href="http://groups.google.com/group/clojure-dev/msg/80ba7fad2ff04708">http://groups.google.com/group/clojure-dev/msg/80ba7fad2ff04708</a> too.</p><p>richhickey said: so, don't use syntax-quote, just use clojure.core/-&gt;</p><p>chouser@n01se.net said: Only -&gt; and -&gt;&gt; are actually legal here anyway &#8211; if you've locally bound foo to -&gt; there's not really any good reason to think (fn <span class="error">&#91;(foo inc a)&#93;</span> a) should work. And if you've redefined -&gt; or -&gt;&gt; to mean something else in your ns, do we need to catch that at compile time, or is it okay to emit the rearranged code and see what happens?</p>
<p>In short, would '#{<del>&gt; -&gt;&gt; clojure.core/</del>&gt; clojure.core/-&gt;&gt;} be sufficient?</p><p>cgrand said: Only -&gt; and -&gt;&gt; are legal here but what if they are aliased or shadowed? Instead of testing the symboil per se I would check, if:</p>
<ul>
<li>the symbol is not in &amp;env</li>
<li>the symbol resolve to #'clojure.core/<del>&gt; or #'clojure.core/</del>&gt;&gt;</li>
</ul>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(when-not (&amp;env (first b)) (#{#'clojure.core/-&gt; #'clojure.core/-&gt;&gt;} (resolve (first b))))</pre>
</div></div>
<p>but it requires to change destructure's sig to pass the env around</p><p>Rich: Are you assigned to this by accident? If so, please deassign yourself.</p>ApprovalVettedGlobal Rank[CLJ-346] (pprint-newline :fill) is not handled correctlyhttp://dev.clojure.org/jira/browse/CLJ-346
Clojure<p>Filled pretty printing (where we try to fit as many elements on a line as possible) is being too aggressive as we can see when we try to print the following array:</p>
<p>user&gt; (binding <span class="error">&#91;*print-right-margin* 20&#93;</span> (pprint (int-array (range 10))))</p>
<p>Produces:</p>
<p>[0,<br/>
1,<br/>
2,<br/>
3,<br/>
4, 5, 6, 7, 8, 9]</p>
<p>Rather than </p>
<p>[0, 1, 2, 3, 4,<br/>
5, 6, 7, 8, 9]</p>
<p>or something like that. (I haven't worked through the exact correct representation for this case).</p>
<p>We currently only use :fill style newlines for native java arrays.</p>CLJ-346(pprint-newline :fill) is not handled correctlyDefectMinorOpenUnresolvedTom FaulhaberAssembla ImporterprintWed, 12 May 2010 09:45:00 -0500Tue, 3 Sep 2013 13:32:12 -0500Backlog01<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/346">http://www.assembla.com/spaces/clojure/tickets/346</a><br/>
Attachments:<br/>
0347-pprint-update-2.diff - <a href="https://www.assembla.com/spaces/clojure/documents/diLxv6y4Sr35GVeJe5cbLr/download/diLxv6y4Sr35GVeJe5cbLr">https://www.assembla.com/spaces/clojure/documents/diLxv6y4Sr35GVeJe5cbLr/download/diLxv6y4Sr35GVeJe5cbLr</a></p><p>stu said: [<a href="file:diLxv6y4Sr35GVeJe5cbLr">file:diLxv6y4Sr35GVeJe5cbLr</a>]</p><p>stu said: The second patch includes the first, and adds another test.</p><p>tomfaulhaber said: This patch was attached to the wrong bug. It should be attached to bug #347. There is no fix for this bug yet.</p><p>Is this current?</p><p>Tom, this patch doesn't apply, and I am not sure why. Can you take a look?</p>ApprovalVettedGlobal RankWaiting Ontomfaulhaber[CLJ-250] debug buildshttp://dev.clojure.org/jira/browse/CLJ-250
Clojure<p>This ticket includes two patches: </p>
<ol>
<li>a patch to set <tt><b>assert</b></tt> when clojure.lang.RT loads, based on the presence of system property clojure.debug</li>
<li>expand error messages in assert to include <tt>local-bindings&lt;/code&gt; (a new macro which wraps the implicit &lt;code&gt;&amp;env</tt>)</li>
</ol>
<p>Things to consider before approving these patches:</p>
<ol>
<li>should there be an easy Clojure-level way to query if debug is enabled? (checking assert isn't the same, as debug should eventually drive other features)</li>
<li>assertions will now be off by default &#8211; this is a change!</li>
<li>is the addition of the name <tt>local-bindings</tt> to clojure.core cool?</li>
</ol>
CLJ-250debug buildsEnhancementMinorOpenUnresolvedUnassignedStuart HallowaybuildWed, 27 Jan 2010 17:40:00 -0600Wed, 23 Oct 2013 03:12:43 -0500Release 1.2Backlog04<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/250">http://www.assembla.com/spaces/clojure/tickets/250</a><br/>
Attachments:<br/>
add-clojure-debug-flag.patch - <a href="https://www.assembla.com/spaces/clojure/documents/aUWn50c64r35E-eJe5aVNr/download/aUWn50c64r35E-eJe5aVNr">https://www.assembla.com/spaces/clojure/documents/aUWn50c64r35E-eJe5aVNr/download/aUWn50c64r35E-eJe5aVNr</a><br/>
assert-report-locals.patch - <a href="https://www.assembla.com/spaces/clojure/documents/aUWqLSc64r35E-eJe5aVNr/download/aUWqLSc64r35E-eJe5aVNr">https://www.assembla.com/spaces/clojure/documents/aUWqLSc64r35E-eJe5aVNr/download/aUWqLSc64r35E-eJe5aVNr</a></p><p>Ignore the old patches. Considering the following implementation, please review and then move ticket to waiting on Stu:</p>
<ol>
<li>RT will check system property <tt>"clojure.debug"</tt>, default to false</li>
<li>property will set the root binding for the current <tt>&#42;assert&#42;</tt>, plus a new <tt>&#42;debug&#42;</tt> flag. (Debug builds can and will drive other things than just asserts.)</li>
<li>does Compile.java need to push <tt>&#42;assert&#42;</tt> or <tt>&#42;debug&#42;</tt> as thread local bindings, or can they be root-bound only when compiling clojure?</li>
<li>will add <tt>&#42;debug&#42;</tt> binding to <tt>clojure.main/with-bindings</tt>. Anywhere else?</li>
<li>build.xml should not have to change &#8211; system properties will flow through (and build.xml may not be around much longer anyway)</li>
<li>once we agree on the approach, I will ping maven plugin and lein owners so that they flow the setting through</li>
<li>better assertion messages will be a separate ticket</li>
<li>what is the interaction between &#42;debug&#42; and &#42;unchecked-math&#42;? Change checks to <tt>(and &#42;unchecked-math&#42; (not &#42;debug&#42;))</tt>}?</li>
</ol>
<p>#3 - root bound only<br/>
#4 - should <b>not</b> be in with-bindings for same reason as #3 - we don't want people to set! &#42;debug&#42; nor &#42;assert&#42;<br/>
#8 - yes, wrapping that in a helper fn</p>
<p>#6 - my biggest reservation is that this isn't yet informed by maven best practices</p><p>System properties can be passed through Maven, so I do not anticipate this being a problem.</p>
<p>However, I would prefer <tt>&#42;assert&#42;</tt> to remain true by default.</p><p>SS is correct about this approach not posing any issue for Maven. In addition, the build could easily be set up to always emit two jars, one "normal", one "debug".</p>
<p>I'd suggest that, while <tt>clojure.debug</tt> might have broad effect, additional properties should be available to provide fine-grained control over each of the additional "debug"-related parameterizations that might become available in the future.</p>
<hr />
<p>I'd like to raise a couple of potentially tangential concerns (after now thinking about assertions for a bit in the above context), some or all of which may simply be a result of my lack of understanding in various areas.</p>
<p>Looking at where <tt>assert</tt> is used in <tt>core.clj</tt> (only two places AFAICT: validating arguments to <tt>derive</tt> and checking pre- and post-conditions in <tt>fn</tt>), it would seem unwise to make it <tt>false</tt> by default. i.e. non-<tt>Named</tt> values would be able to get into hierarchies, and pre- and post-conditions would simply be ignored.</p>
<p>It's my understanding that assertions (talking here of the JVM construct, from which Clojure reuses <tt>AssertionError</tt>) should not be used to validate arguments to public API functions, or used to validate any aspect of a function's normal operation (i.e. <a href="http://download.oracle.com/javase/1.4.2/docs/guide/lang/assert.html#usage">"where not to use assertions"</a>). That would imply that <tt>derive</tt> should throw <tt>IllegalArugmentException</tt> when necessary, and fn pre- and post-conditions should perhaps throw <tt>IllegalStateException</tt> &#8211; or, in any case, something other than <tt>AssertionError</tt> via <tt>assert</tt>. This would match up far better with most functions in core using <tt>assert-args</tt> rather than <tt>assert</tt>, the former throwing <tt>IllegalArgumentException</tt> rather than <tt>AssertionError</tt>.</p>
<p>That leads me to the question: is <tt>assert</tt> (and <tt>&#42;assert&#42;</tt>) intended to be a Clojure construct, or a quasi-interop form?</p>
<p>If the former, then it can roughly have whatever semantics we want, but then it seems like it should not be throwing <tt>AssertionError</tt>.</p>
<p>If the latter, then <tt>AssertionError</tt> is appropriate on the JVM, but then we need to take care that assertions can be enabled and disabled at runtime (without having to switch around different builds of Clojure), ideally using the host-defined switches (e.g. <tt>-ea</tt> and friends) and likely not anything like <tt>&#42;assert&#42;</tt>. I don't know if this is possible or practical at this point (I presume this would require nontrivial compiler changes).</p>
<hr />
<p>Hopefully the above is not water under the bridge at this point. Thank you in advance for your forbearance. <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/wink.gif" height="20" width="20" align="absmiddle" alt="" border="0"/></p><p>Thanks for the useful input Chas. Nothing is concluded yet. I think we should step back and look at the objective here, before moving forward with a solution. Being a dynamic language, there are many things we might want to validate about our programs, where the cost of checking is something we are unwilling to pay in production.</p>
<p>Being a macro, assert has the nice property that, should &#42;assert&#42; not be true during compilation, it generates nil, no conditional test at all. Thus right now it is more like static conditional compilation.</p>
<p>Java assert does have runtime toggle-ability, via &#45;ea as you say. I haven't looked at the bytecode generated for Java asserts, but it might be possible for Clojure assert to do something similar, if the runtime overhead is negligible. It is quite likely that HotSpot has special code for eliding the assertion bytecode given a single check of some flag. I'm just not sure that flag is <a href="http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#desiredAssertionStatus()">Class.desiredAssertionStatus</a>.</p>
<p>Whether this turns into changes in assert or pre/post conditions, best practices etc is orthogonal and derived. Currently we don't have a facility to run without the checks. We need to choose between making them disappear during compilation (debug build) or runtime (track &#45;ea) or both. Then we can look at how that is reflected in assert/pre-post and re-examine existing use of both. The "where not to use assertions" doc deals with them categorically, but in not considering their cost, seems unrealistic IMO. </p>
<p>I'd appreciate it if someone could look into how assert is generated and optimized by Java itself.</p><p>Bytecode issues continue to be above my pay grade, unfortunately…</p>
<p>A few additional thoughts in response that you may or may not be juggling already:</p>
<p><tt>assert</tt> being a macro makes total sense for what it's doing. Trouble is, "compile-time" is a tricky concept in Clojure: there's code-loading-time, AOT-compile-time, and transitively-AOT-compile-time. Given that, it's entirely possible for an application deployed to a production environment that contains a patchwork of code or no code produced by <tt>assert</tt> usages in various libraries and namespaces depending upon when those libs and ns' were loaded, AOT-compiled, or their dependents AOT-compiled, and the value of <tt>&#42;assert&#42;</tt> at each of those times. Of course, this is the case for all such macros whose results are dependent upon context-dependent state (I think this was a big issue with <tt>clojure.contrib.logging</tt>, making it only usable with log4j for a while).</p>
<p>What's really attractive about the JVM assertion mechanism is that it can be parameterized for a given runtime on a per-package basis, if desired. Reimplementing that concept so that <tt>assert</tt> can be <tt>&#42;ns&#42;</tt>-sensitive seems like it'd be straightforward, but the compile-time complexity already mentioned remains, and the idea of having two independently-controlled assertion facilities doesn't sound fun.</p>
<p>I know nearly nothing about the CLR, but it would appear that it doesn't provide for anything like runtime-controllable assertions.</p><p>The best (dated) evidence I could find says that the compiler sets a special class static final field <tt>$assertionsDisabled</tt> based on the return of <tt>desiredAssertionStatus</tt>. HotSpot doesn't do anything special with this, dead code elimination simply makes it go away. The code indeed compiles this way:</p>
<p> 11: getstatic #6; //Field $assertionsDisabled:Z<br/>
14: ifne 33<br/>
17: lload_1<br/>
18: lconst_0<br/>
19: lcmp<br/>
20: ifeq 33<br/>
23: new #7; //class java/lang/AssertionError<br/>
26: dup<br/>
27: ldc #8; //String X should be zero<br/>
29: invokespecial #9; //Method java/lang/AssertionError."&lt;init&gt;":(Ljava/lang/Object;)V<br/>
32: athrow</p>
<p>Even if we were 100% sure that assertion removal was total, I would still vote for a separate Clojure-level switch, for the following reasons:</p>
<ol>
<li>I have a real and pressing need to disable some assertions, and I don't need the Java interop at all. Arguably others will be in the same boat.</li>
<li>there will be multiple debugging facilities over time, and having a top-level <b>debug</b> switch is convenient for Clojure users.</li>
<li>Java dis/enabling via command line flags is still possible as a separate feature. We could add this later as a (small) breaking change to our assert, or have a separate java-assert interop form. I am on the fence about which way to go here.</li>
<li>I believe it is perfectly fine to throw an <tt>AssertionError</tt> from a non-Java-assertion-form. We don't believe in a world of a static exception hierarchy, and an assertion in production is a critical failure no matter what you call it. Even Scala does it <img class="emoticon" src="http://dev.clojure.org/jira/images/icons/emoticons/smile.gif" height="20" width="20" align="absmiddle" alt="" border="0"/> <a href="http://daily-scala.blogspot.com/2010/03/assert-require-assume.html">http://daily-scala.blogspot.com/2010/03/assert-require-assume.html</a></li>
</ol>
<p>Rich: awaiting your blessing to move forward on this.</p><p>The compiler sets $assertionsDisabled when, in static init code? Is there special support in the classloader for it? Is there a link to the best dated evidence you found?</p><ol>
<li>Yes, in static init code</li>
<li>There is no special support in the classloader, per Brian Goetz (private correspondence) last week. But dead code elimination is great: "The run-time cost of disabled assertions should indeed be zero for compiled code"</li>
</ol>
<p>Link: Google "java assert shirazi". (Not posting link because I can't tell in 10 secs whether it includes my session information.)</p><p>Is there anything new on this issue? I also look for a convenient way to disable assertions in production.</p><p>I am also interested in any news on this issue.<br/>
Convenient way to enable/disable assertions at runtime (preferably via -ea/-da options) would be a great feature!</p><p>Btw. there is a library for runtime-toggable assertions available via clojars<br/>
<a href="https://github.com/pjstadig/assertions">https://github.com/pjstadig/assertions</a><br/>
This helped me a great deal.</p>ApprovalVettedGlobal RankWaiting Onrichhickey[CLJ-5] Unintuitive error response in clojure 1.0http://dev.clojure.org/jira/browse/CLJ-5
Clojure<p>The following broken code:</p>
<p>(let [<span class="error">&#91;x y&#93;</span> {}] x)</p>
<p>provides the following stack trace:</p>
<p>Exception in thread "main" java.lang.UnsupportedOperationException: nth not supported on this type: PersistentArrayMap (test.clj:0)<br/>
at clojure.lang.Compiler.eval(Compiler.java:4543)<br/>
at clojure.lang.Compiler.load(Compiler.java:4857)<br/>
at clojure.lang.Compiler.loadFile(Compiler.java:4824)<br/>
at clojure.main$load_script__5833.invoke(main.clj:206)<br/>
at clojure.main$script_opt__5864.invoke(main.clj:258)<br/>
at clojure.main$main__5888.doInvoke(main.clj:333)<br/>
at clojure.lang.RestFn.invoke(RestFn.java:413)<br/>
at clojure.lang.Var.invoke(Var.java:346)<br/>
at clojure.lang.AFn.applyToHelper(AFn.java:173)<br/>
at clojure.lang.Var.applyTo(Var.java:463)<br/>
at clojure.main.main(main.java:39)<br/>
Caused by: java.lang.UnsupportedOperationException: nth not supported on this type: PersistentArrayMap<br/>
at clojure.lang.RT.nth(RT.java:800)<br/>
at clojure.core$nth__3578.invoke(core.clj:873)<br/>
at user$eval__1.invoke(test.clj:1)<br/>
at clojure.lang.Compiler.eval(Compiler.java:4532)<br/>
... 10 more</p>
<p>The message "nth not supported on this type" while correct doesn't make the cause of the error very clear. Better error messages when destructuring would be very helpful.</p>CLJ-5Unintuitive error response in clojure 1.0EnhancementMinorOpenUnresolvedUnassignedAssembla ImportererrormsgsWed, 17 Jun 2009 02:35:00 -0500Fri, 18 Apr 2014 01:45:02 -0500Backlog02<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/5">http://www.assembla.com/spaces/clojure/tickets/5</a></p><p>Please see the attached patch which produces a (hopefully more clear) error message as shown below (given the broken code shown in the original bug report):</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">Clojure 1.4.0-master-SNAPSHOT
user=&gt; (let [x 42 y 43] (+ x y))
85
user=&gt; (let [[x y] {}] x)
UnsupportedOperationException left side of binding must be a symbol (found a PersistentVector instead). clojure.lang.<span class="code-object">Compiler</span>.checkLet (<span class="code-object">Compiler</span>.java:6545)
user=&gt;</pre>
</div></div>
<p>In addition, this patch checks the argument of (let) as shown below:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt; (let 42)
UnsupportedOperationException argument to (let) must be a vector (found a <span class="code-object">Long</span> instead). clojure.lang.<span class="code-object">Compiler</span>.checkLet (<span class="code-object">Compiler</span>.java:6553)</pre>
</div></div><p>Patch produced by doing git diff against commit ba930d95fc (master branch).</p><p>Sorry, this patch is wrong: it assumes that the left side of the binding is wrong - the <tt><span class="error">&#91;x y&#93;</span></tt> in :</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(let [[x y] {}] x)</pre>
</div></div>
<p>because <tt><span class="error">&#91;x y&#93;</span></tt> is a vector, when in fact, the left side is fine (per <a href="http://clojure.org/special_forms#let">http://clojure.org/special_forms#let</a> : "Clojure supports abstract structural binding, often called destructuring, in let binding lists".)</p>
<p>So it's the right side (the {}) that needs to be checked and flagged as erroneous, not the <tt><span class="error">&#91;x y&#93;</span></tt>.</p><p>Add patch better-error-for-let-vector-map-binding</p>
<p>This produces the following:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">(let [[x y] {}] x)
Exception map binding to vector is not supported</pre>
</div></div>
<p>There are other cases that are not handled by this though &#8212; like binding vector to a set</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">user=&gt; (let [[x y] #{}] x)
UnsupportedOperationException nth not supported on <span class="code-keyword">this</span> type: PersistentHashSet</pre>
</div></div>
<p>Wondering if it might be better to try convert the map to a seq to support? Although this might be another issue.</p>
<p>Thoughts?</p><p>This seems too specific. Is this issue indicative of a larger problem that should be addressed? Even if this is the only case where bindings produce poor error messages, all the cases described above should be addressed in the patch. </p><p>Unfortunately, realized that this still does not cover the nested destructuring cases. Coming to the conclusion, that my approach above is not going to work for this.</p><p>File: clj-5-destructure-error.diff </p>
<p>Added support for nested destructuring errors</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">let [[[x1 y1][x2 y2]] [[1 2] {}]]
;=&gt; UnsupportedOperationException let cannot destructure class clojure.lang.PersistentArrayMap.</pre>
</div></div><p>I am not wild about that error message, let can destructure a map fine. </p>
<p>If there error message were to change, I would prefer to get something like "sequential destructing not supported on maps".</p>
<p>I actually like the "nth not supported" error message, because it is exactly the problem, nth, used by sequential destructuring, doesn't work on maps.</p>
<p>it conveys exactly what the problem is if you know how destructing works and what nth means, where as "UnsupportedOperationException let cannot destructure class clojure.lang.PersistentArrayMap" seems misleading when you are in the know</p>ApprovalVettedGlobal RankPatchCode and Test[CLJ-1420] ThreadLocalRandom instead of Math/randomhttp://dev.clojure.org/jira/browse/CLJ-1420
Clojure<p>The standard Math.random() is thread-safe through being declared as a synchronized static method.</p>
<p>The patch uses java.util.concurrent.ThreadLocalRandom which actually seems to be two times faster than the ordinary Math.random() in a simple single threaded criterium.core/bench:</p>
<p>The reason I investigated the function at all was to be sure random-number generation was not a bottleneck when performance testing multithreaded load generation.</p>
<p>If necessary, one could of course make a conditional declaration (like in fj-reducers) based on the existence of the class java.util.concurrent.ThreadLocalRandom, if Clojure 1.7 is to be compatible with Java versions &lt; 1.7</p>
Requires Java &gt;=1.7!CLJ-1420ThreadLocalRandom instead of Math/randomEnhancementMinorOpenUnresolvedUnassignedLinus EricssonmathperformanceSun, 11 May 2014 10:58:43 -0500Fri, 29 Aug 2014 16:50:43 -0500Release 1.7Backlog04<p>Benchmark on current rand (clojure 1.6.0), $ java -version<br/>
java version "1.7.0_51"<br/>
OpenJDK Runtime Environment (IcedTea 2.4.4) (7u51-2.4.4-0ubuntu0.13.10.1)<br/>
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)</p>
<p>:jvm-opts ^:replace [] (ie no arguments to the JVM)</p>
<p>(bench (rand 10)) <br/>
Evaluation count : 1281673680 in 60 samples of 21361228 calls. <br/>
Execution time mean : 43.630075 ns <br/>
Execution time std-deviation : 0.420801 ns <br/>
Execution time lower quantile : 42.823363 ns ( 2.5%) <br/>
Execution time upper quantile : 44.456267 ns (97.5%) <br/>
Overhead used : 3.194591 ns </p>
<p>Found 1 outliers in 60 samples (1.6667 %) <br/>
low-severe 1 (1.6667 %) <br/>
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers </p>
<p>Clojure 1.7.0-master-SNAPSHOT.</p>
<p>(bench (rand 10)) <br/>
Evaluation count : 2622694860 in 60 samples of 43711581 calls. <br/>
Execution time mean : 20.474605 ns <br/>
Execution time std-deviation : 0.248034 ns <br/>
Execution time lower quantile : 20.129894 ns ( 2.5%) <br/>
Execution time upper quantile : 21.009303 ns (97.5%) <br/>
Overhead used : 2.827337 ns </p>
<p>Found 2 outliers in 60 samples (3.3333 %) <br/>
low-severe 2 (3.3333 %) <br/>
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers </p>
<p>I had similar results on Clojure 1.6.0, and ran several different tests with similar results. java.util.Random.nextInt is suprisingly bad. The ThreadLocalRandom version of .nextInt is better, but rand-int can take negative integers, which would lead to some argument conversion for (.nextInt (ThreadLocalRandom/current) n) since it need upper and lower bounds instead of a simple multiplication of a random number [0,1).</p>
<p>CHANGE:</p>
<p>The (.nextDouble (ThreadLocalRandom/current) argument) is very quick, but cannot handle negative arguments. The speed given a plain multiplication is about 30 ns.</p><p>Added some simplistic tests to be sure that rand and rand-int accepts ratios, doubles and negative numbers of various kinds. A real test would likely include repeated generative testing, these tests are mostly for knowing that various arguments works etc.</p><p>0001-rand-using-ThreadLocalRandom-and-tests-for-random.patch contains the changed (rand) AND the test cases.</p><p>Clojure requires Java 1.6.0 so this will need to be reconsidered at a later date. We do not currently have any plans to bump the minimum required JDK in Clojure 1.7 although that could change of course.</p><p>I've always thought that the randomness features in general are of limited utility due to the inability to seed the PRNG, and that a <tt>clojure.core/<b>rand</b></tt> dynamic var would be a reasonable way to do that.</p>
<p>Maybe both of these problems could be partially solved with a standard library? I started one at <a href="https://github.com/fredericksgary/four">https://github.com/fredericksgary/four</a>, but presumably a contrib library would be easier for everybody to standardize on.</p><p>Gary, I'm all for creating some well-thought out random-library, which could be a candidate for some library clojure.core.random if that would be useful.</p>
<p>Please have a look at <a href="http://code.google.com/p/javarng/">http://code.google.com/p/javarng/</a> since that seems to do what you library four does (and more). Probably we could salvage either APIs, algorithms or both from this library.</p>
<p>I'll contact you via mail!</p>
<p>Come to think of it, a <b>rand</b> var in clojure.core shouldn't be a breaking change, so I'll just make a ticket for that to see how it goes. That should at the very least allow solving the concurrency issue with <tt>binding</tt>. The only objection I can think of is perf issues with dynamic vars?</p><p>New issue is at <a href="http://dev.clojure.org/jira/browse/CLJ-1452" title="clojure.core/*rand* for seedable randomness">CLJ-1452</a>.</p><p>Patch 0001-rand-using-ThreadLocalRandom-and-tests-for-random.patch dated May 11 2014 no longer applied cleanly to latest master after some commits were made to Clojure on Aug 29, 2014. It did apply cleanly before that day.</p>
<p>I have not checked how easy or difficult it might be to update this patch. See section "Updating Stale Patches" on this wiki page for some tips on updating patches: <a href="http://dev.clojure.org/display/community/Developing+Patches">http://dev.clojure.org/display/community/Developing+Patches</a></p>ApprovalVettedGlobal RankPatchCode[CLJ-771] Move unchecked-prim casts to clojure.uncheckedhttp://dev.clojure.org/jira/browse/CLJ-771
Clojure<p>Per Rich's comment in <a href="http://dev.clojure.org/jira/browse/CLJ-767" title="Remove support for non-primitive bit-shift operations"><del>CLJ-767</del></a>:</p>
<blockquote><p>Moving unchecked coercions into unchecked ns is ok</p></blockquote>CLJ-771Move unchecked-prim casts to clojure.uncheckedEnhancementMinorOpenUnresolvedUnassignedAlexander TaggartmathThu, 7 Apr 2011 14:37:09 -0500Fri, 30 Jan 2015 14:17:19 -0600BacklogBacklog02<p>Requires that patch on <a href="http://dev.clojure.org/jira/browse/CLJ-782" title="long cast is not checked for Object decimal types"><del>CLJ-782</del></a> be applied first.</p><p>Applies on master as of commit 66a88de9408e93cf2b0d73382e662624a54c6c86</p><p>still considering when to incorporate this</p><p>v2 of the patch applies to master as of commit eccde24c7fb63679f00c64b3c70c03956f0ce2c3</p><p>Patch clj-771-move-unchecked-casts-patch-v3.txt dated Sep 6 2012 is the same as Alexander Taggart's patch move-unchecked-casts.patch except that it has been updated to apply cleanly to latest Clojure master.</p><p>Patch clj-771-move-unchecked-casts-patch-v4.txt dated Oct 20 2012 is the same as Alexander Taggart's patch move-unchecked-casts.patch except that it has been updated to apply cleanly to latest Clojure master.</p><p>The patch clj-771-move-unchecked-casts-patch-v4.txt applies cleanly to latest master and passes all tests. Rich marked this ticket as Incomplete on Dec 9 2011 with the comment "still considering when to incorporate this" above. Is it reasonable to change it back to Vetted or Screened so it can be considered again, perhaps after Release 1.5 is made?</p><p>Patch clj-771-move-unchecked-casts-patch-v5.txt dated Feb 12 2013 is the same as Alexander Taggart's patch move-unchecked-casts.patch except that it has been updated to apply cleanly to latest Clojure master.</p>ApprovalVettedGlobal RankPatchCode and TestWaiting Onrichhickey[CLJ-731] Create macro to variadically unroll a combinator function definitionhttp://dev.clojure.org/jira/browse/CLJ-731
Clojure<p>Clojure contains a set of combinators that are implemented in a similar, but slightly different way. That is, they are implemented as a complete set of variadic overloads on both the call-side and also on the functions that they return. Visually, they all tend to look something like:</p>
<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>(defn foo
([f]
(fn
([] do stuff...)
([x] do stuff...)
([x y] do stuff...)
([x y z] do stuff...)
([x y z &amp; args] do variadic stuff...)))
([f1 f2]
(fn
([] do stuff...)
([x] do stuff...)
([x y] do stuff...)
([x y z] do stuff...)
([x y z &amp; args] do variadic stuff...)))
([f1 f2 f3]
(fn
([] do stuff...)
([x] do stuff...)
([x y] do stuff...)
([x y z] do stuff...)
([x y z &amp; args] do variadic stuff...)))
([f1 f2 f3 &amp; fs]
(fn
([] do stuff...)
([x] do stuff...)
([x y] do stuff...)
([x y z] do stuff...)
([x y z &amp; args] do variadic stuff...))))
</pre>
</div></div>
<p>To build this type of function for each combinator is tedious and error-prone.</p>
<p>There must be a way to implement a macro that takes a "specification" of a combinator including:</p>
<p>1. name<br/>
2. docstring<br/>
3. do stuff template<br/>
4. do variadic stuff template</p>
<p>And builds something like the function <tt>foo</tt> above. This macro should be able to implement the current batch of combinators (assuming that <a href="http://dev.clojure.org/jira/browse/CLJ-730">http://dev.clojure.org/jira/browse/CLJ-730</a> is completed first for the sake of verification).</p>CLJ-731Create macro to variadically unroll a combinator function definitionEnhancementMinorOpenUnresolvedUnassignedFogusWed, 26 Jan 2011 12:00:01 -0600Fri, 26 Jul 2013 10:30:06 -0500Backlog24<p>This seems useful. Rich, would you accept a patch?</p><p>Nevermind, just saw that Rich already suggested this on the dev list. Patch away.</p>ApprovalVettedGlobal Rank[CLJ-291] (take-nth 0 coll) redux...http://dev.clojure.org/jira/browse/CLJ-291
Clojure<p>I dont seem to be able make the old ticket uninvalid so here goes<br/>
(take-nth 0 coll) causes (at least on Solaris) infinite space and time consumption<br/>
It's not a printout error as the following code causes the problem too</p>
<p>(let [j 0<br/>
firstprod (apply * (doall (map #(- 1 %) (take-nth j (:props mix)))))]) ; from my parameter update function</p>
<p>I used jvisualvm and the jvm is doing some RNI call - no clojure code is running at all<br/>
If left alone it will crash the jvm with all heap space consumed<br/>
0 is an InvalidArgument for take-nth<br/>
I wouldnt mind if it produced an infinite lazy sequence of nils even though thats wrong<br/>
It doesnt do this though it actively destroys the JVM<br/>
Its a bug nasty destructive and it took me half a day to figure out what was going on<br/>
please let someone fix it!</p>CLJ-291(take-nth 0 coll) redux...EnhancementMinorIn ProgressUnresolvedUnassignedAssembla ImporterThu, 8 Apr 2010 06:29:00 -0500Sat, 27 Jul 2013 10:25:22 -0500Backlog01<p>Converted from <a href="http://www.assembla.com/spaces/clojure/tickets/291">http://www.assembla.com/spaces/clojure/tickets/291</a><br/>
Attachments:<br/>
fixbug291.diff - <a href="https://www.assembla.com/spaces/clojure/documents/dfNhoS2Cir3543eJe5cbLA/download/dfNhoS2Cir3543eJe5cbLA">https://www.assembla.com/spaces/clojure/documents/dfNhoS2Cir3543eJe5cbLA/download/dfNhoS2Cir3543eJe5cbLA</a></p><p>bhurt said: Before this bug gets marked as invalid as well, let me point out that the problem here is that (take-nth 0 any-list) is a meaningless construction- the only question is what to do when this happens. IMHO, the correct behavior is to throw an exception.</p><p>ataggart said: [<a href="file:dfNhoS2Cir3543eJe5cbLA">file:dfNhoS2Cir3543eJe5cbLA</a>]: throws IllegalArgumentException on negative step size</p><p>Does calling (take-nth 0 ...) cause the problem, or only realizing the result?</p><p>I'm not seeing a problem. Calling take-nth and even partially consuming the seq it returns works fine for me:</p>
<p>(take 5 (take-nth 0 <span class="error">&#91;1 2 3&#93;</span>))<br/>
;=&gt; (1 1 1 1 1)</p>
<p>Note however that it is returning an infinite lazy seq. The example in the issue description seems to include essentially (doall &lt;infinite-lazy-seq&gt;) which does blow the heap:</p>
<p>(doall (range))</p>
<p>This issue still strikes me as invalid.</p><p>(take-nth 0 ...) returns an infinite sequence of the first item:</p>
<p>(take 12 (take-nth 0 <span class="error">&#91;1 2 3&#93;</span>))<br/>
=&gt; (1 1 1 1 1 1 1 1 1 1 1 1)</p>
<p>Is something other than this happening on Solaris?</p>ApprovalVettedGlobal Rank