On Tue, Jul 26, 2011 at 4:56 AM, Sam Aaron <samaa...@gmail.com> wrote:
> Hey Ken,
>
> Thanks for this :-)

You're welcome.

> Actually I was just looking at compare-and-set! just now. This solution seems 
> nicer than Nick's 'place changed/old-val in atom' but still not particularly 
> clean as you have to enter a tight spin loop.

Actually, the swap! function has the same tight spin loop:

https://github.com/clojure/clojure/blob/f704853751d02faf72bd53be599ee0be6c1da63e/src/clj/clojure/core.clj#L2099
https://github.com/clojure/clojure/blob/f704853751d02faf72bd53be599ee0be6c1da63e/src/jvm/clojure/lang/Atom.java#L33

> The additional 'return old and new vals' versions of swap! and reset! that I 
> propose place this machinery at the language level where I believe it 
> belongs. However, I'm also super interested to hear other people's thoughts 
> on this matter...

Complicating the return value for common functions might be problematic.

I wonder if Clojure could benefit from a "secondary value return"
concept like in Common Lisp: the secondary value is ignored unless
captured with a special binding form like (let [a/b (foo)] (println a
b)), which doesn't clash with any existing syntax (currently a/b as a
let symbol is plain invalid since only unqualified names are allowed).
Returning it would be a matter of returning the output of some special
form, say (ret2 a b) instead of just a. (Note that this would have to
knock a (and b) out of tail position for recur; sorry.)

Implementing this under the hood is slightly trickier. Returning a
pair object with a usually-nil right cell for every function return
under the hood would be expensive and wasteful. Ideally existing
functions have unchanged calling convention.

Instead, I'd propose adding PairObj, PairLong, and PairDouble classes
in clojure.lang with public final fields "left" and "right" and a
constructor of two arguments to set them -- right is always Object and
left is Object, long, or double, respectively, along with the first
constructor argument's type. AFn is augmented with another 20 invoke
methods, pairInvoke rather than just invoke, which are implemented to
each call the corresponding regular invoke method and wrap its return
value as follows: new Pair(invoke(...),null);. IFn also grows to
specify these added methods. There's also a pairApplyTo method,
implemented in AFn to do what applyTo does only ending up calling a
pairInvoke method rather than an invoke method.

The compiler emits only the same invoke methods it currently does for
normal functions. Functions with a (ret2 ...) form inside, on the
other hand, get overrides for the pairInvoke methods corresponding to
their arities. The invoke implementations are compiled with all
instances of (ret2 a b) in tail position (ret2, like recur, being
disallowed outside of tail position) replaced by a. The pairInvoke
implementations are corresponding ones with all instances of (ret2 a
b) turned into (Pair. a b), and all outermost tail-position forms x
that aren't (ret2 ...) forms and don't have nested (ret2 ...) forms
wrapped in (Pair. x nil).

The special destructuring binding form is handled by the compiler (so
is available in let* and loop*) to call a pairInvoke method of the
function call form that is the right hand side of the assignment (and
it must be a function call form) and then destructure the pair.

Only the primary return value can be primitive.

Note: ret2 is only a provisional name; someone may well be able to
think of a better one and/or one that's less likely to collide with
other code. Returning more than 2 things should probably not get extra
support; just use a vector and destructure the second thing. (That
suggests allowing (let [a/[b c & d] (foo quux)]) binding forms. That
would require reader support. Or another syntax than a/b for binding
auxiliary return values.)

Note 2: binding auxiliary return values in parameter lists is
problematic (e.g. (defn foo [a/b c] ...) ... (foo
(bar-which-uses-ret2) 42)). The problem is that when foo is called,
the compiler won't know whether to use invoke or pairInvoke on
bar-which-uses-ret2, since it won't in general know whether foo's
first argument is going to be destructured in this way. Defensively
always using pairInvoke and dealing with that in parameter binding
would be inefficient for nearly all function calls. This should
therefore not be attempted; (defn foo [a b c] ...) ... (let [a/b
(bar-which-uses-ret2)] (foo a b 42)) will have to be good enough.

-- 
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

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

Reply via email to