On Mon, Jan 2, 2012 at 2:16 PM, Tassilo Horn <tass...@member.fsf.org> wrote:
> nchurch <nchubr...@gmail.com> writes:
>
> Hi,
>
>> Someone was asking on the list here about multiple return values,
>> which Clojure does not have.
>>
>> If the facility were ever added, perhaps multiple values could be
>> accessed via namespaces.  Functions would possess another level of
>> namespace and have the ability to inject values into the environment
>> under that namespace.  For instance:
>>
>> (defn quotient [x y]
>>      .....
>>      (values quotient remainder))
>>
>> (clojure.core/quotient 5 2)
>> --> 2
>> clojure.core/quotient/remainder
>> --> 1
>>
>> This seems simpler than the Common Lisp way.
>
> I don't think so.  And there are several major problems with that
> approach.  First of all, your clojure.core.quotient/remainder var needs
> to be a mutable, thread-local var, because else you couldn't be sure
> that 1 is the remainder of the quotient call directly above, or the
> remainder of a call that happened a blink later in another thread (or
> ForkJoinTask).
>
> Another problem is that if the var name is determined by the name of the
> local var in the function which is given to `values', then changing its
> name requires changing all places where clojure.core.quotient/remainder
> is used.

On top of all that, how often is this needed anyway where
destructuring or metadata can't solve the problem? When performance
isn't a concern and you control the calling functions (or they're
generic things like map and reduce that just pass the return value
through to code you control, such as whatever processes the map
sequence output) you can replace something with a vector of that
something and additional values, and destructure. And even when you
don't control the calling functions, and replacing the return value
with a wrapper of any kind would blow up, if that return value is a
seq, vector, or other Clojure data type that supports metadata, you
can attach metadata (and, to avoid collisions, use namespaced keywords
in it) to smuggle the extra values out. In a similar vein, if a
non-primitive, non-final Java type is the expected value, you can
subclass it with added fields and getters to smuggle out the extra
values (the old "decorator pattern") using deftype or reify or proxy.
(Some of these don't let you explicitly add fields, but all of them
let you define "getters" that close over locals in the function
returning them. You'll need a definterface specifying the getters,
too.)

When the only objection to destructuring is performance, as is likely
with your quotient-and-remainder case or similar
mathematical-transform cases, there's a final judo move available:
continuation-passing. You make the code that was going to *use* the
results into a *parameter*, which takes all of the results as
arguments. Pre-1.3 you'd probably write something like

(defmacro quot-and-rem [a b q r & forms]
  `(let [a# (long ~a)
         b# (long ~b)
         t# (... a# ... b# ...)
         ~q (...)
         ~r (...)]
     ~@forms))

which would execute forms with q and r bound to the quot and rem,
respectively, of a and b.

Now you can also pass a function expecting two long primitives to a
function that takes two long primitives and a function and calls that
function with two long primitives, the quotient and the remainder.
(The macro approach still gets you the potential benefits and
drawbacks of inlining both functions: avoiding the overhead of calls
and parameter passing, twice, but also making the current function
larger, perhaps large enough to cause cache misses. Obviously it also
lets you stay source-compatible with older Clojure versions, for
whatever that's worth.)

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