Re: Why does unquote clone values?
Hi Atamert, I'm not an expert but I believe REPL does compile the forms you enter to bytecode. It compiles an fn form into a JVM class that implements IFn. It also compiles a quoted form (such as `(fn ...) ) but this time it takes the form of a list (as in (list ...) ), IOW it's still data after it is compiled. The latter is executed directly and the former is evaluated (turned into bytecode and then executed perhaps). So I tried what I probably should have tried in the first place and used the debugger. I think I understand what's going on now. user= [(Object.)] works: [#Object@...] user= (eval `[ ~(Object.) ]) doesn't: CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined It begins by calling clojure.lang.Compiler.analyze on the list (fn* [] (eval (clojure.core/apply clojure.core/vector (clojure.core/seq (clojure.core/concat (clojure.core/list (Object.))) Recursively going through the expression and building up a tree of ConstantExpr, ending up with a Compiler$FNExpr, that is actually compiled to Bytecode, it calls .eval() on this which returns an IFn (which is an instance of the dynamically-created class) and calls .invoke() on that. We end up with an FNExpr with src= the list (that inner vector holds an actual reference to the Object, no strings anywhere) (fn* [] [#Object java.lang.Object@4b2e26cf]) The error occurs when compiling this to Bytecode, specifically in: emitValue( value = #Object java.lang.Object@4b2e26cf ... ) That method explicitly handles emitting bytecode that sits in the static constructor of the IFn for null and constants of type Boolean etc ... ISeq, IPersistentList and Pattern. For all other types it calls RT.printString(value), trying to emit that string as a constant, to be interpreted by readString. As the error message says printString fails for Object. So it's not at all like I suspected: - the unquoting doesn't clone anything, it simply the value in the list. - eval doesn't roundtrip the entire form to String and back. Eval clones everything because you can't embed a pointer to an existing object as a constant in Bytecode. The reason the closure doesn't need to do element-wise comparison in my example is because the pointer to the existing object is passed as a constructor parameter at runtime. f4 is so much slower than f1 despite apparently having the same bytecode is that the constructor for f1 is called with a pointer to the original object, while the constructor to f4 is called inside the eval, with a pointer to a clone of the object. As far as I can tell AOT goes through the same steps and then just saves the generated bytecode in a .class file. There appears to never be any kind of interpretation, it's always generating bytecode and having the JVM evaluate that, even for the most trivial expressions like (inc 1). Sorry for spreading my confusion about this; probably would have been better suited as a blog post. -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Why does unquote clone values?
Hi Atamert, Here's a gist with the example code: https://gist.github.com/neapel/4e502a14e3738b709672 I tried replacing a closure with a dynamically built and evaluated metafunction but discovered that it was actually slower. If evaluating code during run is slower than AOT compiling it, it wouldn't surprise me. I didn't actually try AOT compiling it, just using the REPL. As far as I understand there shouldn't be a difference in speed, since (eval) runs the same compiler as AOT does, and even writes out a .class file. (possibly unrelated, maybe due to eval, when trying to AOT compile the example project by adding `:aot :all` and running `lein jar`, it appears to fall into an infinite loop. The JAR file grew to 8GB, but I couldn't list its contents.) I guess f2 runs this much slower than f1 because it doesn't actually pass a reference to x when unquoting, but clones the value, which means it needs to compare the lists element-wise. This is interesting. Were you able to confirm it or are you just guessing? (I'm just curious.) The code in the Gist produces a nice table now, here's my result (I couldn't reproduce the case where f3 runs as fast as f1 for vectors; but it's actually less weird this way) I think it's quite obvious that f1 compares always references, as does the nil case, while the vec and map cases compare by value: |map |vec | nil | name | |++--+--| | 5 ns | 5 ns | 5 ns | f1 | | 558 ns | 134 ns | 6 ns | f2 | | 567 ns | 130 ns | 7 ns | f3 | | 561 ns | 117 ns | 7 ns | f4 | It also dumps the disassembly, we can see that: f1-map == f1-vec == f1-nil (makes sense, as the reference to the value is passed in the constructor of the closure) f2-map = f3-map, f2-vec = f3-vec, f2-nil = f3-nil (with renaming) f4-map = f1, f4-vec = f1, f4-nil = f1 (with renaming) f2-map = f2-vec, except for calling clojure.lang.RT.map / vector. So there are only three versions of the actual bytecode: - the closure f1/f4; - f2 building a new object in its static constructor - f2-nil using a null constant instead of a field containing null. importantly, f1/f4 are identical: public final class F extends clojure.lang.AFunction { /* ... static constructor getting clojure.core/= ... */ Object x; public F(Object o) { x = o } public Object invoke( Object y ) { /*basically*/ return (bool (= x y)) } } So why does f4 behave almost like f2/f3?! Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Why does unquote clone values?
On Thursday, July 3, 2014 5:19:56 PM UTC+1, adrian...@mail.yu.edu wrote: You're going down a rabbit hole here. Evaluating forms at runtime will always result in a slower execution time than a function that doesn't evaluate a form at runtime. I thought that was the whole point of Clojure? The REPL is just parsing a string or file and running (eval) on it, I can't detect any difference in speed when doing it manually: (quick-bench ((fn fac [x] (if (zero? x) 1 (* x (fac (dec x) 10)) ; = 263 ± 9 ns (eval `(quick-bench ((fn fac# [x#] (if (zero? x#) 1 (* x# (fac# (dec x#) 10))) ; 269 ± 8 ns I'm very sure that the differences I measured are only because the lists are being copied instead of passed by reference, which I can understand in the case where the disassembly actually shows a copy being constructed. But not in the case where the bytecode is exactly the same --- and criterium takes a lot of time to warm up etc, all of the same optimisations should have been applied (and the dynamic part of the code is not a lot anyway, it's just calling AOT-compiled java code anyway. The only difference in timing must stem from the element-wise comparison) -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Why does unquote clone values?
On Thursday, July 3, 2014 6:15:32 PM UTC+1, adrian...@mail.yu.edu wrote: I'm not sure I understand what you're saying here. :( Your example is simply benchmarking the same bit of code in each form. Why would evaluating one explicitly affect that benchmark? Your original example called eval in the body of the functions that you're benchmarking. That is where the overhead comes from. No I'm benchmarking the functions returned by f1-4. They use different ways of generating their return function, i.e. by eval'ing the function constructor (fn [] ...) once before the benchmark -- the `let [f (f1 v)]` part. Of course the call to (eval) takes time, but my point is that a function that was generated using (eval) is exactly the same as one generated statically. Except somehow when unquoting the x-parameter to create the eval'ed form things are being cloned that shouldn't. That's what I'm measuring. Actually I just found a better example that doesn't need timing etc: (def x (Object.)) (str x) ; java.lang.Object@... `(str ~x) ; (clojure.core/str #Object java.lang.Object@...) (eval `(str ~x)) ; RuntimeException: Can't embed object in code, maybe print-dup not defined: java.lang.Object@... (eval `(str x)) ; java.lang.Object@... -- works through resolving user/x So it's not actually a problem with quoting, that returns a list with str and the object as expected. But eval'ing that list fails because it can't print-dup'ed?! I can't tell where that's happening, because it looks like clojure.lang.Parser.eval is actually processing the elements of the list as I expected… -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Why does unquote clone values?
On Thursday, July 3, 2014 7:27:24 PM UTC+1, adrian...@mail.yu.edu wrote: No I'm benchmarking the functions returned by f1-4. Where did I say different? I understood Your original example called eval in the body of the functions that you're benchmarking as meaning I was running something like (quick-bench (eval `( (fn [y] ...) v) )) or (quick-bench ( (eval `(fn [y] ...)) v) ) But I'm actually calling eval before the benchmark and storing the output: (let [f (eval `(fn [y] ...))] (quick-bench (f v))) So the body that's being benchmarked is only the call to the dynamically generated function. Unless I'm totally misunderstanding how Clojure evaluates these things [wouldn't surprise me]. I would really like to help you gain a greater understanding here, but I must admit you are coming off as fairly confrontational, so I am hesitant to continue this conversation. Good luck. Sorry, that wasn't my intention. I kept repeating myself because I'm not sure if I'd be understood (since I'm new to Clojure and don't know the canonical words). Also, there are a lot of other discussions where people use eval in tight loops and wonder why it's so slow; that and the mention of a difference between AOT and eval performance made me think I should make it clear that I'm not wondering why eval is slow, but why it's product behaves differently. Using a benchmark to do so was way too indirect anyway, I believe my problem may have something to do with the failure of (eval `(str ~(Object.))) Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Why does unquote clone values?
Hi, I tried replacing a closure with a dynamically built and evaluated metafunction but discovered that it was actually slower. Here's a minimal example: (defn f1 [x](fn [y ] (=x y ))) (defn f2 [x] (eval `(fn [y#] (= ~x y# (defn f3 [x] (eval `(fn [y#] (= ~@[x] y# (use 'criterium.core) (def v nil) ; nil - same speed. (let [f (f1 v)] (quick-bench (f v))) ; 7.1ns (let [f (f2 v)] (quick-bench (f v))) ; 5.4ns (let [f (f3 v)] (quick-bench (f v))) ; 5.4ns (def v [10 20]) ; vector - f2 slower (let [f (f1 v)] (quick-bench (f v))) ; 6.8ns (let [f (f2 v)] (quick-bench (f v))) ; 78.0ns (let [f (f3 v)] (quick-bench (f v))) ; 5.3ns (def v {10 20}) ; map - f2 and f3 slower?! (let [f (f1 v)] (quick-bench (f v))) ; 6.6ns (let [f (f2 v)] (quick-bench (f v))) ; 151.0ns (let [f (f3 v)] (quick-bench (f v))) ; 152.0ns I guess f2 runs this much slower than f1 because it doesn't actually pass a reference to x when unquoting, but clones the value, which means it needs to compare the lists element-wise. The other cases all compare the references which is why the nil case runs at the same speed. Why does unquoting ~x clone the vector? And why doesn't splice unquoting ~@[x] appear to clone the vector, but *does* clone the map?! Is there a way to pass all values per reference into a quote, to get exactly the same behaviour as f1? Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Why does unquote clone values?
It gets even weirder. I tried this hoping it would create a closure like f1 does: (defn f4 [x] (eval `(let [[x#] ~@[[x]]] (fn [y#] (= x# y#) And indeed using no.disassemble in my test cases f1 and f4 always create the same bytecode: a clojure.lang.AFunction with a field holding the value x, which is passed through the function's constructor. Likewise, f2 and f3 always create the same bytecode: an AFunction without any fields and an empty constructor, with a static constructor creating a new vector/map/nil. All the bodies are identical, calling clojure.lang.Util.equiv, with the argument and the stored reference object. (now talking about the functions returned by f1-4) But: in the vector case, f4 takes 140ns, almost twice of what even f2 takes, and much longer than f1 (which is apparently identical!). It makes no sense that f3 (identical to f2) doesn't take that long. In the map case, f2, f3 and f4 take the same time, 150ns, again making no sense as f1=f4. Is there some metadata I'm missing? -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
How to debug redefinitions of classes
Hi, I'm observing an incredibly weird bug in my program where this happens on the REPL (use 'myapp.some.stuff) (def x (-Record 1)) (type x) ; = myapp.some.stuff.Record (instance? myapp.some.stuff.Record x) ; = false (instance? (type x) x) ; = true Somewhere the namespace gets reloaded, and there actually exist two classes called Record, with the same name, in the same namespace, and the -Record constructor uses one version, but the REPL resolves another version. Is there any way I can tell how this came about? Another example, where the problem is more obvious: (defrecord Foo [x]) ; = user.Foo (def f1 (-Foo 1)) (defrecord Foo [x]) ; = user.Foo (def f2 (-Foo 2)) (type f1) ; = user.Foo (type f2) ; = user.Foo (= user.Foo (type f1)) ; = false (= user.Foo (type f2)) ; = true (instance? Foo f1) ; = false (instance? Foo f2) ; = true Foo on the REPL resolves to the second version, but f1 still uses the first, which still claims to be named the same. This makes things very hard to debug, when i.e. hot reloading some code and see things break in mysterious ways. Usually things resolve themselves when restarting the app, taking care to only load each namespace once, but I somehow created a situation where this doesn't help either. When duplicating classes by reloading code, all metadata is identical, i.e. source location etc; except if you aquire a pointer to the old version of the class instance it's not identical to the new version. Ideally I'd like some kind of *warn-on-redefinition* flag that prints a stacktrace which lead to a class being redefined over an existing one. I tried adding a check to a custom version of defrecord: (defmacro defrecord* [name args] (let [ns-part (namespace-munge *ns*) classname (symbol (str ns-part . name))] (try (resolve classname) ; exists: fail. (throw (IllegalArgumentException. (str Will not redefine classname))) (catch ClassNotFoundException e ; doesn't exist: go ahead. `(let [] ; as before (defrecord ~name ~@args) ; name? (defn ~(symbol (str name '?)) [x#] (instance? ~classname x#)) ; return class ~classname) But this causes all attempts to load the code to fail, (use 'myapp.some.stuff) now always throws the Will not redefine exception, even in a fresh REPL. For some reason everything appears to be loaded twice -- I thought using (ns .. (:require [...])) made sure to only load the namespace if it isn't already loaded, I'm not using :reload / :force-reload?! And (use 'myapp.some.stuff :verbose) only logs a single clojure.core/load as expected; the error is actually thrown after loading clojure.zip?! I assume it's because the compiler runs in a separate thread from the loader? (clojure.core/load /clojure/zip) CompilerException java.lang.IllegalArgumentException: Will not redefine... TL;DR are there more tracing options to figure out what is being (re-)loaded? Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Find source/location of anonymous function by name
Hi, Say I'm getting an ArityException Wrong number of args (2) passed to: myparser/fn--106751 exception. Unfortunately the code is quite heavy on the higher order side, so the stack trace doesn't reveal anything useful, especially since the exception is thrown by the `apply` before, so I'm not even getting line number information. And I don't really want to read a mile of traces or step through every call of `apply`. Is there a direct way to get the source location information, like the stack-trace-printer seems to do, just from the generated name of a closure like myparser/fn--106751? It's not defined as a var, so (source) doesn't work. Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: Find source/location of anonymous function by name
On 10 May 2014, at 18:36, Ambrose Bonnaire-Sergeant abonnaireserge...@gmail.com wrote: Have you tried naming the anonymous functions that might be culprits? (fn trace-me [..] ...) Yes, that turned out to be the quickest way, naming everything with a vim command: :%s/fn \[/\=fn TRACE- . line(.) . [/g Fixing the bug, and then just removing the names again: :%s/TRACE-[0-9]* //g But I'm still interested if there's a pure clojure generated-name to location solution I could just call from the REPL. Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
What does ^{} actually translate to?
Hi, I'm trying to attach metadata to some values (not the vars holding them). I thought ^{x} y was the same as (with-meta y {x}), but while it works for f2/f4, f1's metadata is nowhere to be found, while f3 works as expected: (def f1 doc ^{:x 1} (partial inc)) (meta f1) ; nil, unexpected (meta #'f1) ; contains :doc, but not :x, as expected (def f2 doc ^{:x 2} #(inc %)) (meta f2) ; {:x 2}, as expected (meta #'f2) ; contains :doc, but not :x, as expected (def f3 doc (with-meta (partial inc) {:x 3})) (meta f3) ; {:x 3} (def f4 doc (with-meta #(inc %) {:x 4})) (meta f4) ; {:x 4} Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: clojure.zip: skip a node
On Tuesday, May 6, 2014 4:07:11 PM UTC+1, Alex Miller wrote: I wrote this article long ago which hints about this at the end: https://www.ibm.com/developerworks/library/j-treevisit/ I started from that actually, very helpful article. I have since noticed a bug in my previous skip function where it would loop infinitely when skipping from the rightmost location. The fix includes an end function, so I can no just iterate backwards using that as you suggested. Leaving this here for future reference, in case anybody comes across the same problem: (defn end returns the location loc where (end? (next loc)) is true. [loc] (loop [loc loc] (let [loc (z/rightmost loc)] (if (z/branch? loc) (recur (z/down loc)) loc (defn skip returns the next location that is not a child of this one [start-loc] (loop [loc start-loc] (cond ; can't skip, jump to end (nil? loc) (z/next (end start-loc)) ; at end (z/end? loc) loc ; go to right/up true (or (z/right loc) (recur (z/up loc)) The approach I have taken for editing trees with zippers is to do a post-walk from end to beginning - that way you're always done transforming and will not walk into your edited subtrees. The article does talk a little about how to separate navigation from transformation; it's not particularly hard. You want to start from your rightmost node, which you can get from a repeated application of zip/rightmost or last of zip/rights. Then repeatedly call prev till you reach a node without a parent at that point convert the loc to a node in the termination. I can dig up actual code for this later if you're interested. Alex On Monday, May 5, 2014 6:01:04 PM UTC-5, Pascal Germroth wrote: Hi, I'm using clojure.zip to edit a tree by visiting each location using zip/next, possibly using zip/replace to alter the tree. There are cases where I replace a part of the tree with another tree that will/must not be visited, but I couldn't find a good way to skip nodes, since (zip/next (zip/replace loc new-subtree)) will walk right into my new tree, and I can't use (zip/right (zip/replace loc new-subtree)) as the replaced location might already be the rightmost. Is there a built-in function I missed, or a zip enhancement library I could use? (defn skip returns the next location that is not a child of this one [loc] (if (or (z/end? loc) (nil? loc)) loc (loop [loc loc] (or (z/right loc) (recur (z/up loc)) I came up with this replacement, does that seem like a good idea, or am I using zip completely wrong (because what I really would like to do is iterate backwards through the tree, starting at the end, using zip/prev; but there's also no function to just jump to the end as far as I can tell) Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
clojure.zip: skip a node
Hi, I'm using clojure.zip to edit a tree by visiting each location using zip/next, possibly using zip/replace to alter the tree. There are cases where I replace a part of the tree with another tree that will/must not be visited, but I couldn't find a good way to skip nodes, since (zip/next (zip/replace loc new-subtree)) will walk right into my new tree, and I can't use (zip/right (zip/replace loc new-subtree)) as the replaced location might already be the rightmost. Is there a built-in function I missed, or a zip enhancement library I could use? (defn skip returns the next location that is not a child of this one [loc] (if (or (z/end? loc) (nil? loc)) loc (loop [loc loc] (or (z/right loc) (recur (z/up loc)) I came up with this replacement, does that seem like a good idea, or am I using zip completely wrong (because what I really would like to do is iterate backwards through the tree, starting at the end, using zip/prev; but there's also no function to just jump to the end as far as I can tell) Cheers, -- pascal -- 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 --- You received this message because you are subscribed to the Google Groups Clojure group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.