Author |
Message |
Arthur Chan #1 / 32
|
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 |
|
|
Ken Walt #2 / 32
|
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 |
|
|
Henry Bak #3 / 32
|
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 |
|
|
Wilbur Stree #4 / 32
|
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 |
|
|
#5 / 32
|
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 |
|
|
Preston Brig #6 / 32
|
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 |
|
|
Fergus Henders #7 / 32
|
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 |
|
|
marssaxma #8 / 32
|
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 |
|
|
Joe Bu #9 / 32
|
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 |
|
|
#10 / 32
|
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 |
|
|
steve mcadam #11 / 32
|
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 |
|
|
Pieter Schoenmake #12 / 32
|
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 |
|
|
Herman Rub #13 / 32
|
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 |
|
|
Fergus Henders #14 / 32
|
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 |
|
|
Robert Harl #15 / 32
|
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 |
|
|
Page 1 of 3
|
[ 32 post ] |
|
Go to page:
[1]
[2] [3] |
|