Multiple return values 
Author Message
 Multiple return values

A thought struck me over the weekend while rereading the "Lambda: the
Ultimate <foo>" papers:

Most programming languages allow procedures with multiple arguments
(in some cases allowing them to be used in curried form as well), but
very few languages allow multiple return values, in spite of the fact
that multiple results *are* multiple arguments when you're wearing
CPS-tinted glasses. Why is this?
--




Sun, 03 Oct 1999 03:00:00 GMT  
 Multiple return values

:>Most programming languages allow procedures with multiple arguments
:>(in some cases allowing them to be used in curried form as well), but
:>very few languages allow multiple return values, in spite of the fact
:>that multiple results *are* multiple arguments when you're wearing
:>CPS-tinted glasses. Why is this?

Returning a collection of anonymous results is more complicated than
loading one result into the registers for return.  If the caller isn't
interested in the return values how are they discarded, etc.?  If
structures are allowed to be returned, then the multiple values can be
returned as one object; this takes care of most cases.

Although I rather like Xerox's Mesa language which did allow multiple returns.

Ken Walter
--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

> A thought struck me over the weekend while rereading the "Lambda: the
> Ultimate <foo>" papers:

> Most programming languages allow procedures with multiple arguments
> (in some cases allowing them to be used in curried form as well), but
> very few languages allow multiple return values, in spite of the fact
> that multiple results *are* multiple arguments when you're wearing
> CPS-tinted glasses. Why is this?

Clumsy syntax, mostly, and almost total ignorance of CPS by non-Lisp
people.

Carl Hewitt was probably the biggest promotor of CPS back in the early
1970's, and the Scheme people (Sussman, Steele) took up the cry.
There is (at least) one issue of Lisp & Symbolic Computation devoted
to CPS and its history.

The stack-based languages (Forth, postscript) have always had symmetry
in multiple arguments and multiple returned values.  These languages
typically have 2 (or more) stacks, so that the return points don't
have to be synchronized to the arguments and returned values.  This
provides for a relatively cheap solution for 'tail calling' (lambda
calculus axiom eta).

Some languages provide for multiple arguments by currying, while
others provide for multiple arguments by virtually consing them into a
list or a vector (or both!).  Most languages allow multiple returned
values in the form of a consed up list or vector, but the efficient
implementation of this can require a _huge_ amount of sophistication
and is therefore not usually done.
--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>A thought struck me over the weekend while rereading the "Lambda: the
>Ultimate <foo>" papers:

>Most programming languages allow procedures with multiple arguments
>(in some cases allowing them to be used in curried form as well), but
>very few languages allow multiple return values, in spite of the fact
>that multiple results *are* multiple arguments when you're wearing
>CPS-tinted glasses. Why is this?

I think that the reasoning is that the function returned a completion
value in the return, but any other variables were passed through the
arguments themselves.  Given the call structure of the earlier
computers, (at least the ones that my father worked on), checking to
see if the function completed successfully was typically all that was
passed in the return value.

Wilbur
--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:
> that multiple results *are* multiple arguments when you're wearing
> CPS-tinted glasses. Why is this?

Because until very recently, procedures in nearly every language had
to return their results in a register. There are still very few
languages that allow a return of a variable size result.

Note that returning a pointer to the result is not the same as
returning a variable sized result since the pointer, that which is
actually returned, is fixed size. When you consider how returns are
implemented, this restriction is understandable. But the restriction
against fixed size results that won't fit in registers is very silly.

--
|   S M Ryan, Cupertino CA

--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>Most programming languages allow procedures with multiple arguments
>(in some cases allowing them to be used in curried form as well), but
>very few languages allow multiple return values [,,,]
>Why is this?

A lot of the problem is due to syntax.
Everyobe likes to write things like

        a = b*sin(c)

where sin is a function invocation.
If we define a routine like sincos(x), how are we going to use it
in an expression?

One attractive approach is used by Beta (a European project).
They seem to have rethought a lot of ideas from ground zero.
First off, assignment works left to right, like this

        a + b => c

(I'm approximating the actual symbols, since I can't remember them).

Or

        a + b => c => d

Or

        a + b, c + d => e, f

Or

        sincos(x) => y, z

Or

        x => sincos => y, z

Or

        1, 2 => sumdifrev => AplusB, AminusB, BminusA

where sumdifrev might be defined like

        sumdifrev(int x, int y)
        return x+y, x-y, y-x

Or

        x => sincos => sumdifrev => x, y, z

There used to be a fair amount about the language on the net, along
with some implementations.  Haven't looked in a while.

Preston Briggs
--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>Most programming languages allow procedures with multiple arguments
>(in some cases allowing them to be used in curried form as well), but
>very few languages allow multiple return values, in spite of the fact
>that multiple results *are* multiple arguments when you're wearing
>CPS-tinted glasses. Why is this?

Many if not most programming languages allow procedures with
multiple output arguments.  For example, instead of

        (x, y) = foo(a, b);

in C you can write

        foo(a, b, &x, &y);

and in C++ or Ada you can write

        foo(a, b, x, y);

and (at least to a first approximation) this is no less convenient.

Return values only provide syntactic convenience over and above
output arguments in the single return value case, when you can
avoid naming the result.  For example,

        bar(baz(a));

is more convenient than

        int tmp;
        baz(a, &tmp);
        bar(tmp);

But in the multiple return value case, you're going to have to name
the argument values anyway.  Thus allowing multiple return values
doesn't seem to buy you much over allowing output arguments.

(Note that I'm not saying that allowing multiple return values is
necessarily a bad idea, I'm just giving my guess as to the reasons
for the historical data you observed.)

--

WWW: <http://www.cs.mu.oz.au/~fjh>

--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

> A thought struck me over the weekend while rereading the "Lambda: the
> Ultimate <foo>" papers:

> Most programming languages allow procedures with multiple arguments
> (in some cases allowing them to be used in curried form as well), but
> very few languages allow multiple return values, in spite of the fact
> that multiple results *are* multiple arguments when you're wearing
> CPS-tinted glasses. Why is this?

I think it is an artefact of the origin of the "function" concept. The
idea of a function in maths is that you give it some parameters and it
performs some calculation on them. The result of the calculation is
the return value. It doesn't make sense to have, for example, an
arctangent return more than one value. Given the origins of computing
in maths it is easy to see why this habit has been carried on.

Incidentally, I've been working with an experimental language "Sierra"
which allows multiple inputs and outputs:

define foo =
  (
  in {x, y, z} = integer32
  out {a, b} = boolean16
  ...
  )

The function can still be used in an expression; the idea is that the
parameter list and return values are implicit record structures, so the
expression value can be placed into any variable whose structure matches
the returned values.

-Mars
--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>Most programming languages allow procedures with multiple arguments
>(in some cases allowing them to be used in curried form as well), but
>very few languages allow multiple return values, in spite of the fact
>that multiple results *are* multiple arguments when you're wearing
>CPS-tinted glasses. Why is this?

More languages require multiple return values than you may think,
since any language that permits a function to return a value of record
("struct" in C/C++) type can be considered to return multiple values
(the elements of the record).  It is true that these languages often
make it clumsy to do this (the programmer has to write code to pack
and unpack the values in the record), and languages are often too
insistent on treating records as having a definite layout in memory.

Note that the C++ Standard Template Library contains the pair template
(representing a pair of objects of arbitrary type) and uses it to
implement functions that return two values.
--
-- Joe Buck     http://www.synopsys.com/pubs/research/people/jbuck.html
--




Tue, 05 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:
> Many if not most programming languages allow procedures with
> multiple output arguments.  For example, instead of

>         (x, y) = foo(a, b);

> in C you can write

>         foo(a, b, &x, &y);

These are semantically distinct. The second requires additional notions
such as variables, addresses, assignments, nonlocal side effects,
aliassing, etc.

--
|   S M Ryan, Cupertino CA

[They're different, but I don't think the issues are very different.  Consider
something like (y, x) = foo(x, y), where x and y may be passed by reference.
-John]
--




Thu, 07 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:
> Clumsy syntax, mostly, and almost total ignorance of CPS by non-Lisp
> people.

I agree (though I don't know what CPS is), the syntax is difficult and
the implementation is also nontrivial.  Here is the syntax that I used
in the Evolve language, which I am certain nobody here has ever heard
of since it never made it outside of IBM as far as I know:

(target1,target2,...targetn);(t1,...tn),(t1,...tn),... = func(whatever)

Each parenthesized list contains the variables to which the result
should be assigned.  If the first parenthesized list is followed by a
semicolon, it indicates the "success" result.  Subsequent lists
indicate the corresponding result values.  For example, this:

(a,b);(c,d),e = xxx()

calls xxx(), places the success code in "a" and "b", places the first
result in "c" and "d", places the second result in "e", and discards
any subsequent results.  To call yyy() and place the first and second
results in "f" and "g" you would code: f,g = yyy() If you wanted to
call zzz() and place the second result in "h" you would code: ,h =
zzz()

This syntax resulted from many many hours of trying to figure out
something that would be flexible enough to do the job yet simple
enough to be remembered.  I think it is probably fairly decent for,
what that's worth.

Behind the scenes, it used an identical parameter list for arguments
and results; this consisted of the number of arguments followed by the
datatype of each and a pointer to its data.

The language was intended for distributed apps and had built-in RPC
with data conversion (EBCDIC/ASCII etc), and its own threading
architecture to ensure identical operation on disparate platforms.
The funny thing is that by the time I completed the prototype they had
decided against going after the application-set it was directed at
(helpdesk automation) so it was stillborn.  Whatever.

The thing that is clumsiest (in my opinion) about multiple results is
that the "if (x())" and the "y = x()" scenarios somehow don't seem to
coincide as well with multiple results.  The "success" code is an
exception which always complexificates<sic> things.  I think that if I
were doing it again it would be difficult to decide between the above
syntax and one which caused the return of only a single object which
was an argument/result list.

If anyone knows of other/better syntaxes for multiple results I would
be interested if they were posted.  -steve
--
MFC GUI Toolkit --> http://home.sprynet.com/sprynet/smcadams
--




Thu, 07 Oct 1999 03:00:00 GMT  
 Multiple return values

   I think it is an artefact of the origin of the "function" concept. The
   idea of a function in maths is that you give it some parameters and it
   performs some calculation on them. The result of the calculation is
   the return value. It doesn't make sense to have, for example, an
   arctangent return more than one value.

What about a function R x R -> R x R?

[What follows is a general remark in this thread, not specifically a
followup to this posting].  Packing multiple return values into a
struct is not the same as a multiple return.  Multiple return means
that a function returns more than one thing, not that it returns a
single thing containing more than one thing.  Also, viewing multiple
return values as a single return value plus a bunch of `out' arguments
is dictated by the compiler implementation targeted view of language
designers.

Since a lot of postings in this thread give the workaround to multiple
return values in their favourite language, I tell y'all how it's done
in TOM, a language I'm developing.

In TOM, a method wishing to return multiple values can collect those
values into a tuple (and, indeed a tuple has a type being the tuple type
consisting of the types of its elements):

(int, int)
  divmod (int, int) (a, b)
{
  return (a / b, a % b);

Quote:
}

You can see there's something weird with the arguments to the method:
the arguments A and B are packed into a tuple and preceding that is
the tuple type the tuple (A, B) should have.  This mechanism of
packing arguments is identical to packing the return values, since, in
fact, TOM method names can consist of multiple parts, like in
Smalltalk and Objective-C.  For example, the following is a String
method which searches for the string S within the range (START, LEN):

(int, int)
  rangeOfString String s
          range (int, int) (start, len);

The value returned is a tuple with the start of the occurence and the
length of the match.  The length will be -1 if no match is found.  If
I am only interested in whether S is contained in the receiving
object, I can use the following code:

        int l;

        (, l) = [some_string rangeOfString s range (0, -1)];
        if (l != -1)
          {
            /* Match... */
          }

I'm not interested in the first int returned by the method, hence the
empty first element on the lhs of the assignment.

One other possible use of tuples is, of course, simultaneous assignment:

        (a, b) = (b, a);

And one final remark: tuples in TOM are not first-class types: the type of
a variable can not be a tuple type.

More information on TOM is available at http://tom.ics.ele.tue.nl:8080/.
--Tiggr
--
The advantage of using an unknown programming language is that
you don't have to answer questions on how it compares to Java.
--




Thu, 07 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>>Most programming languages allow procedures with multiple arguments
>>(in some cases allowing them to be used in curried form as well), but
>>very few languages allow multiple return values, in spite of the fact
>>that multiple results *are* multiple arguments when you're wearing
>>CPS-tinted glasses. Why is this?

>Many if not most programming languages allow procedures with
>multiple output arguments.  For example, instead of
>    (x, y) = foo(a, b);
>in C you can write
>    foo(a, b, &x, &y);

This shows immediately that this is an inferior notation.  It forces x
and y to be stored, as well as almost forcing a subroutine call.

Quote:
>and in C++ or Ada you can write
>    foo(a, b, x, y);
>and (at least to a first approximation) this is no less convenient.

This is a little better, but still has the atrocious prenex syntax.
SOME compilers will permit inlining the process, but even this can run
into problems with optimization, as well as generally not allowing
compiler local treatment of special cases.

A good example of something which is quite easy in hardware but
difficult in software, and which is now often not available on
computers, is

        (exp, mant) =UP. float;

This would be the unpacking operation.  Another is the use of
upper and lower products for a multiplication.

Quote:
>Return values only provide syntactic convenience over and above
>output arguments in the single return value case, when you can
>avoid naming the result.  For example,
>    bar(baz(a));
>is more convenient than
>    int tmp;
>    baz(a, &tmp);
>    bar(tmp);

There is that store and load again, as well as putting the address of
tmp on the stack.  But it would be a good idea to have temporaries,
and to write something like

        tmp = x baz y;
        z = bar(tmp);

If the notation for temporaries is short enough (computer people do
not seem to like the short notation which mathematicians use; it does
make reading much easier for humans, even if not for computers), this
is almost as easy as writing

        z = bar(x baz y);

Quote:
>But in the multiple return value case, you're going to have to name
>the argument values anyway.  Thus allowing multiple return values
>doesn't seem to buy you much over allowing output arguments.
>(Note that I'm not saying that allowing multiple return values is
>necessarily a bad idea, I'm just giving my guess as to the reasons
>for the historical data you observed.)

Anything which can be done on one computer can be done on any other,
if time and space is not of importance.  With good computer parsing,
the languages should be for people to use easily and efficiently, and
not just for those who do not understand.  This is why we need quick
ways of producing readable code for any operation which the user can
handle, whether or not the compiler writer or language designer has
thought of it.  We WILL need a larger character set for this which can
be used in a WYSIWYG mode if the hardware can handle it, and the
corresponding tools.

The number of keystrokes to do infix or similar coding in the language
of a real or virtual machine is few more than using HLLs, if sensible
syntax is used, and the user can write macros with arbitrary syntax.
--
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907-1399

--




Thu, 07 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>> Many if not most programming languages allow procedures with
>> multiple output arguments.  For example, instead of

>>         (x, y) = foo(a, b);

>> in C you can write

>>         foo(a, b, &x, &y);

>These are semantically distinct. The second requires additional notions
>such as variables, addresses, assignments, nonlocal side effects,
>aliassing, etc.

Well, what do you want -- do you want multiple return values for
efficiency, or for syntactic convenience, or both?

The issues you raise are mostly issues for C, because C doesn't really
have output mode arguments (you have to use pointers to simulate
them).  These issues don't cause problems in languages such as Ada,
Sather, and Mercury.  Using output mode arguments in these languages
give you both efficiency (the compiler can easily return multiple
results in registers) and syntactic convenience.

Herman Rubin complained about the "attrocious prenex syntax" of output
mode arguments, and while I think "attrocious" is a too strong, and I
don't think that there is necessarily anything wrong with using prefix
notation for this sort of thing, he does have a point: it certainly
helps if you can easily tell which arguments are input and which are
output.  In the development environment I use, the mode declarations
are only a single keystroke away, but there is certainly an argument
for putting annotations for output arguments on the call, not just on
the procedure declaration.

The SML approach of providing nice syntax for functions that return
tuples can work too, but it requires a more sophisticated compiler to
make it efficient.  Using a different calling convention for such
functions is not too difficult, but in a language with support for
Hindley/Milner style polymorphism like SML, the compiler needs to do a
bit of fancy footwork when you take the address of such a function.

I suppose the approach that Pieter Schoenmakers described is a
compromise; in his language "Tom", tuples are not first-class types,
and so it would probably be easy for a compiler to return tuples of
results in registers.  But I think I'd prefer tuples to be first-class
types.

--

WWW: <http://www.cs.mu.oz.au/~fjh>

--




Sat, 09 Oct 1999 03:00:00 GMT  
 Multiple return values

Quote:

>[...]
>A good example of something which is quite easy in hardware but
>difficult in software, and which is now often not available on
>computers, is

>    (exp, mant) =UP. float;

>This would be the unpacking operation.  [...]

Difficult?  Not available?  What about something like:

  unsigned u;
  int exp, mant;
  union { unsigned u; float f; } uf;

  uf.f = f; u = uf.u;
  exp = (int)(u>>23 & 255U)-127;
  mant = (int)(u &~ 0xFF000000U | 0x800000U);
  if (u>>31) mant = -mant;

?
Sticking to the topic of the thread, wrap this up in:

typedef struct { int exp, mant; } pair;

static pair unpack(float f) {
  pair p;

  [...as above...]

  p.exp = exp; p.mant = mant;
  return p;

Quote:
} /* end unpack */

GCC will optimise this just fine.

-- Rob.
--




Sat, 09 Oct 1999 03:00:00 GMT  
 
 [ 32 post ]  Go to page: [1] [2] [3]

 Relevant Pages 

1. ???Eiffel idiom for multiple return values???

2. Tuples, iterators, and multiple return values in Eiffel?

3. multiple return values

4. Multiple return values

5. Newbie on multiple return values

6. multiple return values for functions

7. Multiple return values

8. 1st-class method closures (was Re: Multiple return values)

9. C-interface and multiple return values

10. multiple Return values

11. Destructuring / pattern-matching (was: Multiple return values)

12. Destructuring / pattern-matching (was: Multiple return values)

 

 
Powered by phpBB® Forum Software