On Wed, Feb 16, 2011 at 3:36 AM, Marko Topolnik <marko.topol...@gmail.com> wrote: >> HOFs and >> lazy seqs add a bit more expense. Try deliberately throwing an >> exception in lazy-seq and then (first (map this (map that ... >> (my-exception-throwing-lazy-seq)))) and see how deep the stack trace >> nests; the method call overheads do add up when there's many of them! > > Tell me about it... these stacktraces are my daily bread. Once we had > a misterious StackOverflowError in the middle of our non-recursive > code. The culprit turned out to be concat, iteratively applied to an > open-ended amount of vectors.
A situation where you have little choice but to apply doall. I don't suppose there's any chance a future version will change nested map/concats on single seqs (at least) to somehow flatten the call stack? For example, (map foo (map bar x)) could emit the same thing as (map (comp foo bar) x) by doing something conceptually like this: (if (and (= (count seqs) 1) (is-map-derived-seq? (first seqs))) (let [f2 seqs] (take-apart-map (first seqs))] (apply map (comp f f2) seqs))) (original-map-code-goes-here)) This moves the problem down into comp, where something similar can be done with suitable compiler support: (let [fs (loop [fs (seq fs) out []] (if fs (let [f (first fs)] (if (is-comp-derived-fn? f) (recur (concat (take-apart-comp f) (next fs)) out) (recur (next fs) (conj out f)))) out))] (make-comp-derived-fn fs)) That would be the *entirety* of comp; that last function has to be a primitive that for two functions outputs bytecode equivalent to #(f2 (f1 args)), for three #(f3 (f2 (f1 args))), and so on. For one argument it should just return the argument without wrapping it in anything and for zero return clojure.core/identity. With those, any amount of nested maps would result in a bounded number of stack frames between seq realizer and the fn supplied to the innermost map. The scheme can be expanded to concat (and therefore to mapcat) easily enough, for instance by making concat produce a Concat object that implements ISeq and is internally just a list of its component seqs; I could actually write this as a deftype in ten minutes and have it tested and fairly likely bug-free in twenty. The concat function would then construct one and use a loop very similar to the one above to flatten nested Concats into the parent. In fact, this suggests the above loop be factored out into a separate selectively-flatten function, something like (defn selectively-flatten "Produces a lazy sequence of elements of coll, except that elements for which flatten-elt? returns true are recursively expanded into subsequences by subseq: (defn selectively-flatten "Produces a lazy sequence of elements of coll, except that elements for which flatten-elt? returns true are recursively expanded into subsequences by subseq: (selectively-flatten vector? identity [1 2 [3 4 5] 6 '(7 8)]) will return '(1 2 3 4 5 6 (7 8))." [flatten-elt? subseq coll] (let [step (fn step [colls] (lazy-seq (if colls (let [c (first colls)] (if c (let [e (first c)] (if (flatten-elt? e) (step (cons (seq (subseq e)) (cons (next c) (next colls)))) (cons e (step (cons (next c) (next colls)))))) (step (next colls)))))))] (seq (step [(seq coll)])))) which the comp replacement can call as (selectively-flatten is-comp-derived-fn? take-apart-comp fs) and the concat replacement as (selectively-flatten #(instance? Concat %) take-apart-concat colls). (This last function has been tested moderately, including on the example in its docstring, and seems to work. Its output is very similar to that of tree-seq but it only emits the leaf nodes. I suppose (remove branch? (tree-seq branch? children root)) might do as an implementation or a replacement though it ought to be a touch less efficient due to additional nested function calls.) > When a new version of Clojure comes out, I routinely invest enough > time to read the new API docs cover-to-cover, searching for new > interesting stuff. The problem is, some important changes don't happen > through new API, but through subtle improvements in the behavior of > existing API. For example, as of 1.2 we have something Rich named > "fine-grained locals clearing" -- a killer feature for lazy seqs, but > not mentioned anywhere in the docs and not easily googlable, unless > you already heard the phrase. That's not even an API change -- it's a compiler change. An improved API behavior would be making nth work on sorted-sets and sorted-maps. ;) > The feature you mention -- functions > that accept primitive arguments -- seems to fall in this class. I > continually worry that my code might be jumping through nonexistent > hoops that I only imagine due to my incomplete understanding of > Clojure. > > I think it is important to clearly document all such features, even > more so than the API, where the source code is the ultimate, easily > reachable documentation. Compiler/language features that aren't clojure.core API features, you mean? The reference pages, like Sequences and Java Interop and Special Forms, document some of this. Recent Changes ought to document changes of this sort from older versions, but would currently documents the changes between 1.1 and 1.2 if it did. Instead it seems to document changes to the *web site*. Perhaps rename that Recent Site Changes and make Recent Changes do this, and Upcoming Changes document the changes between the current stable version and the development branch? In that case the improved locals clearing would be in Recent Changes, the changes to primitive math/functions that take primitives would be in Upcoming Changes (and could be something of a moving target, at least until the spec for 1.3's exact feature set settled down), and the current content of Recent Changes would be in Recent Site Changes. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en