On Feb 15, 2009, at 4:44 PM, Chouser wrote:
> > Here's an example of what I think will be the worst kind of breakage > resulting from changing the meaning of rest from > seq-on-the-next-item-if-any-else-nil to > possibly-empty-collection-of-the-remaining-items: > > (defn my-interpose [x & coll] > (loop [v [x] coll coll] > (if coll > (recur (-> v (conj (first coll)) (conj x)) (rest coll)) > v))) > > This is a bit like the builtin interpose, except it takes multiple > args instead of a collection, and it returns a vector with the > interposed value surrounding all the others: > > (my-interpose 'x 'a 'b 'c) > -> [x a x b x c x] > > At least that's what it does in svn 1282 trunk. In 1282 lazy branch, > it's an infinite loop. Can you spot the problem? > > When discussing this yesterday in IRC, I was pretty firmly against > Rich's preferred names, for exactly this reason. And worse than > trying to fix my own code would be the potential confusion over which > versions of examples, libs, etc. work with which versions of Clojure. While not knowing if sample code has been ported will still be an issue, anyone following the porting recipe: http://clojure.org/lazier#toc7 will avoid this one as well, as the call will be to next, not rest. > > > ...but my position has softened, as I tried to construct an example > for this post that actually broke in a bad way. My first several > attempts produced code that worked in both versions. > > For example, my-interpose above takes multiple args so that I could > safely assume that 'coll' is a seq. My first (unposted) version took > a collection as a second argument, but in that case a simple > "(if coll" is probably already an error, in case a user passed in an > empty vector, or some other collection. The solution would be to test > the seq of the coll: > > (defn my-interpose [x coll] > (loop [v [x] coll coll] > (if (seq coll) ; Don't assume coll is a seq-or-nil > (recur (-> v (conj (first coll)) (conj x)) (rest coll)) > v))) > > That also happens to solve the lazy-branch infinite-loop problem -- > what's more correct in trunk is more correct in lazy, in this case. > So I kept refining my-interpose, trying to get a version that was > correct in trunk but caused a non-exception error in lazy. After > several iterations I finally got the one at the top of this post. > > ...but even that one can be caught easily by turning on > clojure.assert-if-lazy-seq, in which case you get an exception > pointing directly to the line that needs to be changed: > > java.lang.Exception: LazySeq used in 'if' > > So the same changes that will already have to be made to nil puns for > the other seq functions would now have to be made for uses of the new > 'rest' function. I would just clarify that to say that the best route is *not* to structurally change code that uses rest, just have it call next instead (unless you are writing a lazy-seq body). Using next is going to let you preserve your code structure and yields the simplest idioms - since next (still) nil puns! core.clj e.g. is full of code that presumes it is walking a seq chain, and so contains lots of next calls: http://code.google.com/p/clojure/source/diff?spec=svn1282&r=1282&format=side&path=/branches/lazy/src/clj/clojure/core.clj There's nothing wrong with that idiom. I do not recommend that people leave their rest calls and 'fix' the nil puns - instead, change your rest calls to next, then deal with your own lazy-cons calls (possibly restoring some rest calls in lazy-seq bodies), then try the clojure.assert-if-lazy-seq flag to find any conditional use of lazy sequences. Rich --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/clojure?hl=en -~----------~----~----~----~------~----~------~--~---
