roelof@idca.tds.PHILIPS.nl (R. Vuurboom) (07/07/89)
In article <13961@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes: [>> Herman Rubin writes] > >Is there anything really evil with this: > q = divrem(top, bottom, &remainder); Well, since you ask...yes. A cardinal sin that a language can commit is being incapable of expressing a relevant concept adequately. In this case we are acting on one pair of concepts (top,bottom) to produce values for another pair of concepts (quotient,remainder). What we need here is the capability of expressing _adequately_ (read elegantly) the idea of acting on one concept pair to generate values for a second concept pair. A basic language architectural tenet is that logical (semantic) grouping should lead to syntactic grouping and vice verse. Any of the notations below would satisfy this q r <- t/b [q,r] = t/b t b / => q r /t,b/ >> (q,r) However q <- t/b r doesn't. Confer quotient = div(top,bottem,&remainder) Lest you consider this some quaint academic hand waving I will hasten to point out that this problem trundles straight down into your implementation. For this I'll pull out my second language architectural tenet and that is: evaluation (interpretation so you desire) should follow syntactic grouping. The syntactic expression q r <- t/b does not obligate nor disallow parallel evaluation of q and r. If the underlying implementation (computer architecture) allows simultaneous calculation with attendant efficiencies all the better. On the other hand the syntactic expression q <- t/b r obligates a prior calculation of t b and r before that of q. (I'm assuming non-lazy evaluation here). Even if the implementation can parallelize the calculation of q and r the language specification ought to specify a partial ordering (viz first t/b and r then q). The implementation could in fact parallelize (as you point out) but _only_ if the end result is not differentiable from the partially ordered evaluation. But if the language _relies_ on such a parallelization I would consider it broken. >>Since the hardware allows simple things like this [viz parallel evaluation], the language should [support this]. > >The language does. It just doesn't allow you to do it with the >syntax you want. The _language_ doesn't. Because it can't group the concepts correctly. >OK, so the terminology for C is a little broken. I've used Not terminology ...syntax. And perhaps inadequate is more accurate than broken. >languages that have (for example) German words for keywords. >That didn't mean that i felt the need to change the language's >syntax or terminology. > Fortunately, (some) others do. Developing adequate expression to enable clear, concise, complete and _relevant_ conceptualization has always been a major breakthrough in any intellectual endeavour. Anyway weren't most of our parents onto us about not using bad language? :-) -- Roelof Vuurboom SSP/V3 Philips TDS Apeldoorn, The Netherlands +31 55 432226 domain: roelof@idca.tds.philips.nl uucp: ...!mcvax!philapd!roelof
' Sabatella) (07/10/89)
> function "\" (in x, y:integer) returns integer is begin ... end; > >Disregarding the bad syntax; this is not allowed. In Ada you can only >redefine the predefined operators (with some exceptions). So a >declaration for "\" is not allowed. Would you believe I meant "/" - honest, I really did!
pds@quintus.UUCP (Peter Schachte) (07/13/89)
In article <147@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes: >In article <13961@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes: >>Is there anything really evil with this: >> q = divrem(top, bottom, &remainder); >Well, since you ask...yes. >A basic language architectural tenet is that logical (semantic) grouping >should lead to syntactic grouping and vice verse. Any of the notations below >would satisfy this > q r <- t/b [followed with variations on this basic style] There's a big problem with this: you have an expression that's no good for anything but putting on the right side of an assignment. What good are expressions if you can't nest them? APL, as someone else pointed out, could handle this just fine, since you would be returning an array instead of a scalar, and APL can handle arrays very well (if I'm not mistaken, encode is exactly (a generalization of) this quotient/remainder function). And this sort of thing would be ok in forth, since there's nothing wrong with leaving more than one result on the stack. But C (and Pascal, and Modula-2, and most procedural languages) just aren't equipped to handle this. All is not lost, though. In C, you can have divrem(top, bottom, "ient, &remainder) and something similar is possible with VAR parameters in Pascal (and Modula-2, I think). This is symmetrical and reasonably elegant. More elegant, I think, than complicating a language with multiple value expressions without adding enough power to do anything other than assign them to something. CommonLisp, you may point out, does have multiple returns. In fact, CommonLisp is a good example of the problem I'm pointing out. Basically, there's not much you can do with a multiple-value expression except ignore all but the first value (which happens if you nest a MV expression within a function call) and assign its results to variables. The problem here is not just syntax, it's the semantics of multiple value returns. -- -Peter Schachte pds@quintus.uucp ...!sun!quintus!pds
cik@l.cc.purdue.edu (Herman Rubin) (07/13/89)
In article <1207@quintus.UUCP>, pds@quintus.UUCP (Peter Schachte) writes: > In article <147@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes: > >In article <13961@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes: > >>Is there anything really evil with this: > >> q = divrem(top, bottom, &remainder); > >Well, since you ask...yes. > >A basic language architectural tenet is that logical (semantic) grouping > >should lead to syntactic grouping and vice verse. Any of the notations below > >would satisfy this > > q r <- t/b > [followed with variations on this basic style] Even without architecture, I agree with it as a matter of language. The idea of a replacement statement is that the quantities being replaced are determined by the arguments for the expression, which are not themselves changed except by the replacement. We sometimes allow address modification, etc., to go on, but these are side effects to be used with caution. What is so difficult about the idea that a process can have several results? > There's a big problem with this: you have an expression that's no good > for anything but putting on the right side of an assignment. What good > are expressions if you can't nest them? That YOU cannot see a use for them is irrelevant at best. Enough people have posted that they can see a use for this aspect of computer language to include it. I will not object to your including something in the language which I might not think even appropriate. ..................... >More > elegant, I think, than complicating a language with multiple value > expressions without adding enough power to do anything other than assign > them to something. There are times for elegance and times not to have elegance. I will not use an elegant definition if it obscures the concept. I will not use a short elegant proof, in general, if the understanding of the theorem is compromised. I will use a short computational procedure, however, because in computation, time is the major issue. > Basically, there's not much you can do with a multiple-value expression > except ignore all but the first value (which happens if you nest a MV > expression within a function call) and assign its results to variables. More than that can be done. Results can be assigned to registers. Not having nesting would, to me, be a minor inconvenience. Not having multiple values returned can be a major slowing down of the computational procedure. > The problem here is not just syntax, it's the semantics of multiple > value returns. > So there are some things one cannot do with strings of values. I can even think of situations where the string is just what I want to pass to the next stage. -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)
maa@nbires.nbi.com (Mark Armbrust) (07/13/89)
In article <1207@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >In article <147@ssp1.idca.tds.philips.nl> roelof@idca.tds.PHILIPS.nl (R. Vuurboom) writes: >>In article <13961@haddock.ima.isc.com> suitti@haddock.ima.isc.com (Stephen Uitti) writes: >>>Is there anything really evil with this: >>> q = divrem(top, bottom, &remainder); >>Well, since you ask...yes. >>A basic language architectural tenet is that logical (semantic) grouping >>should lead to syntactic grouping and vice verse. Any of the notations below >>would satisfy this >> q r <- t/b >[followed with variations on this basic style] > . . . > >All is not lost, though. In C, you can have > > divrem(top, bottom, "ient, &remainder) How about: typedef struct _divResult { int q; int r; } divResult; extern divResult divide (int, int); main() { divResult qr; int i, j; . . . qr = divide (i, j); /* qr.q is quotient, qr.r is remainder */ . . . } If the compiler can't do a good enough job on divide(), it is trivial to write in assembly language. In the case of MSC 5.1, the return values from divide() are in registers, so there is very little overhead using this method. divide: push bp mov bp, sp mov ax, [bp+4] cwd idiv word ptr [bp+6] pop bp ret ; qr.q returned in ax, qr.r in dx -- Mark Armbrust maa@nbires.nbi.com maa@nbires.UUCP
nick@lfcs.ed.ac.uk (Nick Rothwell) (07/14/89)
In article <1406@l.cc.purdue.edu>, cik@l.cc (Herman Rubin) writes: >In article <1207@quintus.UUCP>, pds@quintus.UUCP (Peter Schachte) writes: >> There's a big problem with this: you have an expression that's no good >> for anything but putting on the right side of an assignment. What good >> are expressions if you can't nest them? > >That YOU cannot see a use for them is irrelevant at best. Enough people >have posted that they can see a use for this aspect of computer language >to include it. I will not object to your including something in the >language which I might not think even appropriate. I see the point of multiple returns, but agree with Peter than having them allowed only in a restricted context is a big problem. >There are times for elegance and times not to have elegance. I will not >use an elegant definition if it obscures the concept. I will not use a >short elegant proof, in general, if the understanding of the theorem is >compromised. Doesn't elegance imply clarity and understanding? How can something obscure and unfathomable be elegant? I don't believe that multiple returns are at all difficult. You need garbage collection since you're passing stuctures out of scope, that's all. >Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Nick. -- Nick Rothwell, Laboratory for Foundations of Computer Science, Edinburgh. nick@lfcs.ed.ac.uk <Atlantic Ocean>!mcvax!ukc!lfcs!nick ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ Fais que ton reve soit plus long que la nuit.
gateley@m2.csc.ti.com (John Gateley) (07/16/89)
In article <1207@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >CommonLisp, you may point out, does have multiple returns. In fact, >CommonLisp is a good example of the problem I'm pointing out. >Basically, there's not much you can do with a multiple-value expression >except ignore all but the first value (which happens if you nest a MV >expression within a function call) and assign its results to variables. Common Lisp is much more powerful than this: multiple-value-call multiple-value-prog1 multiple-value-bind multiple-value-setq Multiple-value-setq is the mv assignment being discussed. MVBind is the binding construct which takes several values, creates variables and binds the values to the variables. MVProg1 is like a begin expression, except it returns (as multiple values) all the values returned by the FIRST expression in the body. MVCall allows 'nesting of expressions', it calls a function using multiple values. Someone else claimed that returning multiple values requires garbage collection. This is not correct. Multiple values are implemented as an extension of the way a single value is returned, and do not inherently require GC; they are not a separate structure. In my experience with Common Lisp, multiple values are useful, but not terribly so. One `problem' with them is that a set of values is easy to percieve as a structure, but this is not really true. They are just values returned on the stack (or however functions/expressions return values), and most things I want to do with them requires removing them from the stack to a separate structure. This is not a problem with multiple values, it is a problem with my perception of what they do. In the divrem example being discussed for example, many of the problems center around using the quotient and remainder after they have been returned. But the only things it makes sense to do with them is either return them as values to someone else, or unpackage them and use them. After unpackaging them, they are no longer multiple values, and should not be thought of as such. John gateley@m2.csc.ti.com
anw@maths.nott.ac.uk (Dr A. N. Walker) (07/18/89)
[Good contributions from, inter alia, cik@l.cc.purdue.edu (Herman Rubin), pds@quintus.UUCP (Peter Schachte), roelof@idca.tds.PHILIPS.nl (R. Vuurboom), and suitti@haddock.ima.isc.com (Stephen Uitti) on the problem of "q r <- t/b".] I think there are several issues here, which are in danger of confusing each other. (a) Is it a language deficiency if "q r <- t/b" cannot be sensibly written? Answer: yes. Solutions on the lines of q = t/b; r = t%b; /* "and the optimiser should tidy up" */ are inadequate because I might want to write records[i++] errcount <- write(fd,buf,BIGBSIZE)/BSIZE after which records[i++] = write(fd,buf,BIGBSIZE)/BSIZE; errcount = write(fd,buf,BIGBSIZE)%BSIZE; is erroneous and { int temp = write(fd,buf,BIGBSIZE); records[i++] = temp / BSIZE; errcount = temp % BSIZE; } is becoming gross (and much harder to optimise). (b) Should all possible machine-code short-cuts be expressible within the language? Answer: yes. "asm" and its relatives almost provide the solution here. I know of several quite old languages in which the embedded machine code could include expressions, provided that certain restrictions were observed. Where it matters, there are ways of making this portable. (c) Should languages permit "functions with several results"? Answer: I don't know what the question means, but the answer is yes. In languages that are (as part of their definition) stack-based, it's very natural for a function to push several objects onto the stack. For more traditional languages, it's harder to see how the function can be defined sensibly unless the objects are grouped in some way; but it is clearly a language deficiency if structures and arrays (as well as pointers thereto) cannot be returned from functions. Many popular languages are defective in this way; some are not. (d) How should "q r <- t/b" actually be written? Answer: form should follow function. Reasonably, the right-hand side should be either a library function (presumably inlined for efficiency) or, equivalently, an infix operator: "divrem (t, b)" or "t DIVREM b", according to taste. The problem is with the left-hand side. Unless I've missed a trick, I don't think there is any way in either C or Algol [my arbitors of taste in such matters!] of bundling up "q" and "r" into an appropriate "lvalue". That is, there is no way of defining "bundle" in Algol (for example) such that bundle (q, r) := t DIVREM b can assign sensibly to "q" and "r". It's easy, and left as an exercise for the reader [:-)], to define in Algol an operator "ASSIGN" such that bundle (q, r) ASSIGN t DIVREM b does the right thing, but it's not looking quite so nice. There are other solutions, but they start looking like the `evil' "q = divrem (t, b, &r)". Note that CPL would have permitted q, r := t/b, t%b from which q, r := t DIVREM b is quite a small step, and well within the Algol/Pascal/C tradition. -- Andy Walker, Maths Dept., Nott'm Univ., UK. anw@maths.nott.ac.uk
quiroz@cs.rochester.edu (Cesar Quiroz) (07/20/89)
This could go to rec.humor.funny, but so would many of the ostensibly serious postings on language design. In an otherwise quite sane contribution to the debate on multiple value returns for the Algol family, we find this little gem (taken sufficiently out of context) that seems to summarize the language design philosophies of some of the frequent posters to this group: `I don't know what the question means, but the answer is yes.' -- Cesar Augusto Quiroz Gonzalez Department of Computer Science University of Rochester Rochester, NY 14627
cliff@ficc.uu.net (cliff click) (07/20/89)
In article <1989Jul17.184707.415@maths.nott.ac.uk>, anw@maths.nott.ac.uk (Dr A. N. Walker) writes: > [Good contributions from, inter alia, cik@l.cc.purdue.edu (Herman Rubin), > pds@quintus.UUCP (Peter Schachte), roelof@idca.tds.PHILIPS.nl (R. Vuurboom), > and suitti@haddock.ima.isc.com (Stephen Uitti) on the problem of "q r <- t/b".] > > (a) Is it a language deficiency if "q r <- t/b" cannot be sensibly > written? Answer: yes. Solutions on the lines of > > q = t/b; r = t%b; /* "and the optimiser should tidy up" */ > > are inadequate because I might want to write [deleted expressions with side effects...] Perhaps what we want is the ability to return a structure for any function call (even an infix or inlined one), and the ability to assign that structure to a mongrel structure we create on the fly. Given: struct{ float q, r; } divrem( float t, b ); and: float q[10], r[10], a[10], b[10]; we get: struct{ q[3], r[9] } = divrem( a[2], b[3] ); or: struct{ q[i++], r[j++] } = divrem( a[i], b[j] ); The lvalue is a structure of lvalues, each of the correct type for the structure but perhaps changing with each usage. This ability could be used to *pass* a structure we create on the fly, instead of filling in the fields in a temp structure: struct timething { int h, m, s }; void printime( struct timething foobar ); int hour, min, sec; [expressions using/setting hour, min, sec...] printime( struct timething { hour, min, sec } ); Flames, anybody? -- Cliff Click, Software Contractor at Large Business: uunet.uu.net!ficc!cliff, cliff@ficc.uu.net, +1 713 274 5368 (w). Disclaimer: lost in the vortices of nilspace... +1 713 568 3460 (h).
peter@ficc.uu.net (Peter da Silva) (07/20/89)
In article <5096@ficc.uu.net>, cliff@ficc.uu.net (cliff click) writes: > or: struct{ q[i++], r[j++] } = divrem( a[i], b[j] ); > printime( struct timething { hour, min, sec } ); This has been suggested before. I like it, thought I think the following syntax is a bit more consistant: (struct {float q, r}) { q[i++], r[j++] } = divrem( a[i], b[j] ); printime( (struct timething) { hour, min, sec } ); execv("/bin/sh", (char *){"sh", "-c", command}); -- Peter da Silva, Xenix Support, Ferranti International Controls Corporation. Business: peter@ficc.uu.net, +1 713 274 5180. | "A char, a short int, and Personal: peter@sugar.hackercorp.com. `-_-' | an int bit-field were walking Quote: Have you hugged your wolf today? 'U` | through the forest..."
robison@m.cs.uiuc.edu (07/20/89)
> bundle (q, r) := t DIVREM b
It is possible to define a `bundle' function in C++ that would create
a pair of references. The `=' operator could be overloaded to
make the above work. The only drawback is that C++ does not have
parametric polymorphism, so a different bundle would have to be
defined for each type of q,r. A polymorphic language like Russell
that distinguishes lvalues from rvalues would really solve the problem.
Arch D. Robison
University of Illinois at Urbana-Champaign
UUCP: {pur-ee,convex}!uiucdcs!robison
Internet: robison@CS.UIUC.EDU
rcd@ico.ISC.COM (Dick Dunn) (07/21/89)
anw@maths.nott.ac.uk (Dr A. N. Walker) writes: . . . > (a) Is it a language deficiency if "q r <- t/b" cannot be sensibly > written? Answer: yes... This brash conclusion is fair warning that unwarranted generalizations are to be found. A "deficiency" in a language must be judged in context of the intended use of the language. Therefore a general statement that this is a deficiency is wrong...in a language where the need is rare, no deficiency exists. > (b) Should all possible machine-code short-cuts be expressible within > the language? Answer: yes... If you have asm's, you can write lots of machine-code shortcuts assuming the hooks are provided to give you access to variables, parameters, etc. But this is not "within the language" in any useful sense! The "asm" facility is explicitly an escape from the language. > ...I know of several quite old languages in which the embedded > machine code could include expressions, provided that certain restrictions > were observed. Where it matters, there are ways of making this portable. The notation can be made quite portable, but the instructions themselves cannot. What's going on here--was this an assertion that we can have portable machine language, or only that we can posit a relatively portable notation for assembly language? > (c) Should languages permit "functions with several results"? Answer: > I don't know what the question means, but the answer is yes... That does it! This is complete nonsense! > ...it's harder to see how the function can be defined sensibly > unless the objects are grouped in some way; but it is clearly a language > deficiency if structures and arrays (as well as pointers thereto) cannot be > returned from functions. Many popular languages are defective in this way; > some are not. Again, per above, it is a deficiency if the facility is needed for the intended problem domain of the language. In many cases being able to return a pointer is sufficient, and being able to return an array is at odds with other design goals of the language. I wish all the would-be language designers would show some more care about their suggestions. It's bad enough that they shoot at a very tiny area of the language without a thought for how the pieces will fit together...and here, without even a thought for how to solve the tiny target problem. -- Dick Dunn rcd@ico.isc.com uucp: {ncar,nbires}!ico!rcd (303)449-2870 ...Simpler is better.
cik@l.cc.purdue.edu (Herman Rubin) (07/21/89)
In article <15941@vail.ICO.ISC.COM>, rcd@ico.ISC.COM (Dick Dunn) writes: > anw@maths.nott.ac.uk (Dr A. N. Walker) writes: > . . . > > (a) Is it a language deficiency if "q r <- t/b" cannot be sensibly > > written? Answer: yes... > > This brash conclusion is fair warning that unwarranted generalizations are > to be found. Considering that many people have already claimed this to be a deficiency, the statement that this is a brash conclusion is even more brash. Since (except possibly for recent developments) every grade school graduate knew that a division had a quotient and remainder, why is it brash to require that this can be sensibly written in a computer language. > A "deficiency" in a language must be judged in context of the intended use > of the language. Therefore a general statement that this is a deficiency > is wrong...in a language where the need is rare, no deficiency exists. That someone who cannot see the need for something does not make it reasonable to call the need rare. Many others have argued that it is not rare. And we have the railroad paradox; if the train will not stop, nobody wanting to use it will be at the station. > > (b) Should all possible machine-code short-cuts be expressible within > > the language? Answer: yes... > > If you have asm's, you can write lots of machine-code shortcuts assuming > the hooks are provided to give you access to variables, parameters, etc. > But this is not "within the language" in any useful sense! The "asm" > facility is explicitly an escape from the language. The asm facility is a massively clumsy escape from the language. Also, most compilers make the interface fantastically difficult. It is generally necessary to do massive editing of the assembler output. Besides, the assembler notation is almost deliberately designed to make it difficult for people to use. And if I am going to use an instruction an appreciable number of times, why should I not be able to write it in a sensible manner? > > ...I know of several quite old languages in which the embedded > > machine code could include expressions, provided that certain restrictions > > were observed. Where it matters, there are ways of making this portable. > > The notation can be made quite portable, but the instructions themselves > cannot. What's going on here--was this an assertion that we can have > portable machine language, or only that we can posit a relatively portable > notation for assembly language? We can have semi-portable machine language, and extremely flexible notation not just for assembler instructions, but also for HLL operations not now included. A few examples of HLL operations not now expressible conveniently. The "&~" operation, hardware on many (most?) machines. Some compilers on some machines will handle the symbol pair. The power operator. Not to be confused with the power function. The power operator is highly overloaded. Unpacking floating point, and the resulting packing. This frequently requires an integer type longer than long. And do it all without loading and storing everything which should stay in registers. > > (c) Should languages permit "functions with several results"? Answer: > > I don't know what the question means, but the answer is yes... > > That does it! This is complete nonsense! Possibly he should have said that there is more than one meaning to the question, but the answer is yes. A replacement procedure takes the quantities on the right side, performs the indicated manipulations, and replaces the variables on the left side by the appropriate values. There is no specification of the order of the replacement, that the quantities are in any way to have any more cohesion than just a list, or that at any time there is to be a single machine entity (other than scattered registers and memory locations) containing the results. So what is so brash about asking for that? > > ...it's harder to see how the function can be defined sensibly > > unless the objects are grouped in some way; but it is clearly a language > > deficiency if structures and arrays (as well as pointers thereto) cannot be > > returned from functions. Many popular languages are defective in this way; > > some are not. Well, I do not have this problem. A list of results is not that hard to understand. > Again, per above, it is a deficiency if the facility is needed for the > intended problem domain of the language. In many cases being able to > return a pointer is sufficient, and being able to return an array is at > odds with other design goals of the language. > > I wish all the would-be language designers would show some more care about > their suggestions. It's bad enough that they shoot at a very tiny area of > the language without a thought for how the pieces will fit together...and > here, without even a thought for how to solve the tiny target problem. I wish the designers of the languages would give up the arrogance that they know everything that the user will want to use. It is usually very difficult to add things to a language after the language has been produced, just as it can be very clumsy to do something in software which could have been cheaply included in hardware. I know of no language produced with the idea of even reasonably efficient code which will allow me to add new operator symbols-- I see now more reason why I should have to write pow(x,y) for x to the y-th power than you should have to write diff(x,y) for x-y. Even Fortran found it necessary to invent an infix notation for that. And do not tell me to write that part of my code in Fortran and another part in C. I may want to use both constructs in the same expression, and I expect that not to be unusual. One can avoid using features of a language which one does not wish to use, but the opposite is not the case. Not all primitive operations are expressed in the languages. And because of this, some of these operations are disappearing from the hardware. A lot more flexibility is available for little cost. I will not be so arrogant as to attempt to tell you everything which should be in a language. I do not even know what I might want to use tomorrow. Languages should be designed by people with this in mind. > Dick Dunn rcd@ico.isc.com uucp: {ncar,nbires}!ico!rcd (303)449-2870 > ...Simpler is better. Sometimes. And what is simpler to you may not be to me. The order of pedagogical presentation of mathematics is far from unique, and I assure you that there is no unanimity on which way is best. -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)
ken@cs.rochester.edu (Ken Yap) (07/22/89)
In article <1430@l.cc.purdue.edu> Herman Rubin writes: | [bla, bla bla] Yawn...
ray@philmtl.philips.ca (Raymond Dunn) (07/22/89)
I missed the start of this discussion, I hope the following is a relevant contribution. In the POP-2 language and its derivatives, multi-result functions are handled in a very straight forward manner: Assignment in POP-2 is left to right, e.g. 5 -> x; Thus to assign the result of a single result function one can do: foo(x) -> y; To assign the results of a multi-result function then, one does: foo(x) -> y -> z; with no practical limitations on the number of results that can be returned in this way. [someone] wrote: >> There's a big problem with this: you have an expression that's no good >> for anything but putting on the right side of an assignment. What good >> are expressions if you can't nest them? In POP-2 this is not the case, if foo is a function of n arguments and bar is a function of one argument and n results, then: foo(bar(x)); is meaningful. Indeed if foo is a function of *one* argument and bar is a function of two results, then: foo(bar()->x); is "equivalent" to: bar() -> x -> y; foo(y); etc. (I don't hold this up as pretty useage, only as a demonstration of the feature). If you think of "f(x,y)" as "push x, push y, call f", and "-> x" as "pop x", then the underlying [stack] mechanism and its usages should be self evident. The types of arguments and results of functions are also not restricted in any way, a value of any type may be passed and returned (integer, real, array, record [structure], function, list etc). As a POP-2 programmer of old, I *still* find it a pain in 'C' to be forced into creating unnecessary structures (or, god forbid, using global variables) just to be able to return multiple values. -- Ray Dunn. | UUCP: ..!uunet!philmtl!ray Philips Electronics Ltd. | TEL : (514) 744-8200 Ext: 2347 600 Dr Frederik Philips Blvd | FAX : (514) 744-6455 St Laurent. Quebec. H4M 2S9 | TLX : 05-824090
jeff@aiai.ed.ac.uk (Jeff Dalton) (07/22/89)
In article <1207@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >CommonLisp, you may point out, does have multiple returns. In fact, >CommonLisp is a good example of the problem I'm pointing out. >Basically, there's not much you can do with a multiple-value expression >except ignore all but the first value (which happens if you nest a MV >expression within a function call) and assign its results to variables. Well, I often do other things with them.
jeff@aiai.ed.ac.uk (Jeff Dalton) (07/22/89)
In article <1207@quintus.UUCP>, pds@quintus.UUCP (Peter Schachte) writes: > There's a big problem with this: you have an expression that's no good > for anything but putting on the right side of an assignment. What good > are expressions if you can't nest them? What good is Prolog?
diamond@csl.sony.JUNET (Norman Diamond) (07/24/89)
In article <5098@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: >I like it, thought I think the following >syntax is a bit more consistant: > > execv("/bin/sh", (char *){"sh", "-c", command}); Maybe execv("/bin/sh", (char *) &{"sh", "-c", command}); might be more consistEnt? -- -- Norman Diamond, Sony Computer Science Lab (diamond%csl.sony.jp@relay.cs.net) The above opinions are inherited by your machine's init process (pid 1), after being disowned and orphaned. However, if you see this at Waterloo or Anterior, then their administrators must have approved of these opinions.
cliff@ficc.uu.net (cliff click) (07/24/89)
In article <1430@l.cc.purdue.edu>, cik@l.cc.purdue.edu (Herman Rubin) writes: > I know of no language produced with the idea of even > reasonably efficient code which will allow me to add new operator symbols-- > I see now more reason why I should have to write pow(x,y) for x to the y-th > power than you should have to write diff(x,y) for x-y. Even Fortran found > it necessary to invent an infix notation for that. Forth can do this fairly simply. In fact, a great deal of the flexability of Forth stems from it's "roll-your-own" syntax abilities. Forget trying to add infix operators to C or Fortran; they have *way* to much momentum. I think Ada allows you to define new (or redefine existing) infix operators. > A replacement procedure takes the quantities on the right side, performs > the indicated manipulations, and replaces the variables on the left side > by the appropriate values. There is no specification of the order of the > replacement, that the quantities are in any way to have any more cohesion > than just a list, or that at any time there is to be a single machine > entity (other than scattered registers and memory locations) containing > the results. I've posted this idea in comp.lang.c and gotten some good feedback. Maybe you need to hire some compiler writer to stuff this into gnu C. If the people of the C world find this handy, maybe it will start creeping into commercial C compilers. The idea is to basically build a psuedo-structure from disjoint variables, to be structure-assigned from a function (infix or otherwise) which returns a structure. For example, we have: struct foobar { double quo, rem }; struct foobar divrem( double num, denom ); double a, b, c, d; ... struct foobar { a, b } = divrem( c, d ); Here a and b can be replaced with the lvalue of any expression yielding a double, and thus can be used to step through arrays. In my example no foobar structure has storage defined for it. The final usage is merely a language convention to lump together the list of variables that are the result of the divrem function. -- Cliff Click, Software Contractor at Large Business: uunet.uu.net!ficc!cliff, cliff@ficc.uu.net, +1 713 274 5368 (w). Disclaimer: lost in the vortices of nilspace... +1 713 568 3460 (h).
pds@quintus.UUCP (Peter Schachte) (07/27/89)
In article <605@skye.ed.ac.uk> jeff@aiai.UUCP (Jeff Dalton) writes: >In article <1207@quintus.UUCP>, pds@quintus.UUCP (Peter Schachte) writes: >> What good are expressions if you can't nest them? >What good is Prolog? Prolog is very good (1/2 :-) [for those who don't get the joke, Quintus sells Prolog systems]. Prolog avoids this whole problem by using relational, rather than functional, notation. This means that multiple values may be returned by a Prolog procedure in exactly the same way as as single value is. -- -Peter Schachte pds@quintus.uucp ...!sun!quintus!pds
pds@quintus.UUCP (Peter Schachte) (07/27/89)
In article <1989Jul17.184707.415@maths.nott.ac.uk> anw@maths.nott.ac.uk (Dr A. N. Walker) writes: > I might want to write > records[i++] errcount <- write(fd,buf,BIGBSIZE)/BSIZE >after which > records[i++] = write(fd,buf,BIGBSIZE)/BSIZE; > errcount = write(fd,buf,BIGBSIZE)%BSIZE; >is erroneous and > { int temp = write(fd,buf,BIGBSIZE); > records[i++] = temp / BSIZE; > errcount = temp % BSIZE; } >is becoming gross (and much harder to optimise). Absolutely. But how about: divrem(write(fd,buf,BIGBSIZE), BSIZE, &records[i++], &errcount); It is essentially the same as your multiple return example, and doesn't have the same perils. It does have the flaw that errcount can't be a register variable. It is conceivable, though, that a global optimizer could put errcount into a register. The problem with multiple returns is filling out the semantics. As I see it, there are two basic ways to handle this: 1. multiple results are packed up into a single structure to be returned. To complete the semantics of this, you only need to specify the semantics of passing such a structure as argument to a function (or primitive like +). One sensible solution is to consider the first of the multiple results to be passed as argument, and the remaining results to be "passed around" the function. For example, if /% returns a quotient and remainder as values, and + returns one value, then: (x/%y)+z would return two values: x/y+z and x%y. I think this semantics could be made to work. I don't really think it's very useful, though. The problem is that usually, when you have multiple results, you want to do different things with them. In order to do that with this sort of multiple values, you have to assign them to separate variables. If you're going to do that anyway, why not just write functions that take pointers indicating where to store the results, as I showed above? 2. The other approach is to treat multiple results as separate things. Forth, Pop-2, and CommonLisp do this. Forth and Pop-2 store results, as well as arguments, on a stack. This gives them great flexibility with multiple results; in effect, multiple results are handled just like a single result. CommonLisp is a bit less flexible*. I am a bit skeptical of Pop-2's approach: foo(divrem(x,y)) (where divrem returns both quotient and remainder, and foo takes two arguments) calls foo with x/y and x%m as its two arguments. It may be pure prejudice, but I'm uncomfortable having two arguments to a function come from the same expression. In any case, it's not terribly useful, since there's a good chance I'll want the arguments in the other order, or I'll want another argument between them, or something. I belive Pop-2 does provide stack manipulation primitives, so this can be handled, but it's going to look pretty grungy. (The same arguments apply to Forth, except that Forth doesn't have the expression notation. This sort of thing is the way one programs in Forth, so this kind of code looks no different than any other Forth code.) It seems to me that the cleanest way to handle multiple results is to just always put them into variables. And this is better done by passing pointers to where to put the results than by muddling the semantics of expressions with multiple returns. Or alternatively, go to a pure stack dicipline, where you always manipulate the stack to get the result(s) from some invocations into others. ---------------- * CommonLisp has 5 forms for handling multiple returns, but none of them allow you to get multiple results from one function call into different function calls without putting them into variables first. For example, suppose CommonLisp had a divrem function, and suppose I wanted to compute x/y*10+log(x%y). I don't see how to do this conveniently without putting x/y and x%y into temporary variables. Maybe I'm just missing it. This is not a condemnation of CommonLisp. Since CommonLisp does not have VAR parameters (to functions) like Pascal or an address-of operator like C, returning multiple values and putting them into variables is the most sensible way to handle multiple returns. -- -Peter Schachte pds@quintus.uucp ...!sun!quintus!pds
cik@l.cc.purdue.edu (Herman Rubin) (07/27/89)
In article <1223@quintus.UUCP>, pds@quintus.UUCP (Peter Schachte) writes: > In article <1989Jul17.184707.415@maths.nott.ac.uk> anw@maths.nott.ac.uk (Dr A. N. Walker) writes: Only selected parts of the posting are commented. > It does have the flaw that errcount can't be a > register variable. It is conceivable, though, that a global optimizer > could put errcount into a register. Any programmer understanding the machine may wish to force register. I do not know of any machine/language combination for which this operation can be done efficiently by providing an address. > The problem > is that usually, when you have multiple results, you want to > do different things with them. In order to do that with > this sort of multiple values, you have to assign them to > separate variables. Usually that is the case. However, suppose you have a high-order iterative solver, say using a function and its first four derivatives. Now what is usually done is to make 5 function calls to get those arguments. But a single call returning those 5 values is likely to cost far less than two function calls returning one value. So the 5-output result can be passed to another subroutine intact. But I agree. What one should do is assign them to temporaries, which is what a compiler does in evaluating a complicated expression. Again, for efficiency, these usually should not be stored and reloaded. > Why > not just write functions that take pointers indicating where > to store the results, as I showed above? Same remarks about registers as above. In the case of evaluating expressions not involving subroutine calls, the compiler or programmer usually assigns the register locations. It is definitely not the same as passing pointers. > 2. The other approach is to treat multiple results as separate > things. Forth, Pop-2, and CommonLisp do this. Forth and > Pop-2 store results, as well as arguments, on a stack. This > gives them great flexibility with multiple results; in > effect, multiple results are handled just like a single > result. CommonLisp is a bit less flexible*. It is neat, but agains stacks are expensive. This may be a reasonable way to do things for an expensive function call, but not for managing hardware expressions. > > It seems to me that the cleanest way to handle multiple results is to > just always put them into variables. I agree that this is a clean way. But it is rarely an efficient way, just as subroutine calls are rarely efficient ways of handling relatively short procedures. If we point some of these things out, we can get programmers who will code efficiently with little effort. -- Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907 Phone: (317)494-6054 hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)
pardo@june.cs.washington.edu (David Keppel) (07/27/89)
In article <1223@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >[Multiple return semantics] >[1. Packed in to a single structure to return.] >[2. Put them in the actual variables.] >[Cleanest: just use VAR parameters.] > divrem(write(fd,buf,BIGBSIZE), BSIZE, &records[i++], &errcount); Even in languages without explicit aliasing, aliasing still occurs. This is a case in point. When I pass VAR parameters to this function, I expect them to be changed when the function returns. But what if the function changes a result that is also used as an input value? Most languages don't pass arrays by value, they pass them by reference. Aliasing problem number one: foo (x, &x[2], &y[2]); ... foo (int i[], int *j, int *k) { *j = i[2]+1; *k = i[2]+2; /* But what if i[2] is (*j)? */ } The first point here is that `j' and `k' aren't really VAR parameters, they are Ada's OUT parameters. If this is truly a function and the value of *j depends on the old value of *j, then that value should be passed separately. To make `foo' work correctly, it is necessary to disallow aliasing somehow. One way is a `noalias' qualifier that says "`j' and `k' may not be aliases for ay part of `i'". If the compiler has this information at the call site, it can often (not always) verify noalias, and can always enforce noalias. Another way is to have multiple return values whose semantics are like those of single return values. ;-D on ( You've won a RETURN VALUE, Mrs. Smith! ) Pardo -- pardo@cs.washington.edu {rutgers,cornell,ucsd,ubc-cs,tektronix}!uw-beaver!june!pardo
peter@ficc.uu.net (Peter da Silva) (08/02/89)
In article <10595@riks.csl.sony.JUNET>, diamond@csl.sony.JUNET (Norman Diamond) writes: > In article <5098@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: > >I like it, thought I think the following syntax is a bit more consistant: > > execv("/bin/sh", (char *){"sh", "-c", command}); > Maybe execv("/bin/sh", (char *) &{"sh", "-c", command}); I don't know. Possibly, but then: execv("/bin/sh", &my_array); Would be more consistent too. Should this construct be seen as an array or as a structure? If you're using it as an array, then it shouldn't need an address operator, but if you're doing: ioctl(fd, TIOCSETC, (struct termio *)&{...}); then it should... > might be more consistEnt? I have this problem with spelling "dependent" too. -- Peter da Silva, Xenix Support, Ferranti International Controls Corporation. Business: peter@ficc.uu.net, +1 713 274 5180. | "The sentence I am now Personal: peter@sugar.hackercorp.com. `-_-' | writing is the sentence Quote: Have you hugged your wolf today? 'U` | you are now reading"
db@lfcs.ed.ac.uk (Dave Berry) (08/04/89)
In article <1223@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: > >The problem with multiple returns is filling out the semantics. As I >see it, there are two basic ways to handle this: > > 1. multiple results are packed up into a single structure to be > returned. To complete the semantics of this, you only need > to specify the semantics of passing such a structure as > argument to a function (or primitive like +). The ML/Hope approach is to have a tuple type. For example, divrem could return values of the type "int * int". Components can be selected by pattern matching or by selector functions. In Standard ML the syntax is "#1 (x divrem y)" to select the first result of x divrem y. Multiple arguments to (uncurried) functions don't exist; you have to create a tuple and pass that, e.g. "plus (2, 3)". Of course, if the "plus" function immediately breaks down the tuple by pattern matching the compiler should optimise away the tuple creation. You can also pass a tuple argument to an infix operator by temporarily negating its infix status, e.g. "(op +) (x divrem y)". One advantage of tuple types over C structures is that you don't have to declare special types - they're just there. > 2. The other approach is to treat multiple results as separate > things. I am a bit skeptical of Pop-2's approach: > > foo(divrem(x,y)) > > (where divrem returns both quotient and remainder, and foo > takes two arguments) calls foo with x/y and x%m as its two > arguments. It may be pure prejudice, but I'm uncomfortable > having two arguments to a function come from the same > expression. The equivalent expression in ML passes a single value around, but this will be optimised away. This is just as flexible as Pop-2, but perhaps you find it easier to cope with? (In some cases Pop-2's stack manipulation primitives may be more flexible than ML). > In any case, it's not terribly useful, since > there's a good chance I'll want the arguments in the other > order, or I'll want another argument between them, or > something. It doesn't handle all cases, but I find it useful. Of course you can assign the multiple returns to identifiers in ML, as in "let val (a, b) = x divrem y in ... end". It's nice not to have to do this when it's not needed. >It seems to me that the cleanest way to handle multiple results is to >just always put them into variables. And this is better done by passing >pointers to where to put the results than by muddling the semantics of >expressions with multiple returns. If you already have pattern matching then multiple results doesn't complicate the semantics at all. Dave Berry, Laboratory for Foundations db%lfcs.ed.ac.uk@nsfnet-relay.ac.uk of Computer Science, Edinburgh Uni. <Atlantic Ocean>!mcvax!ukc!lfcs!db Rhetoric 101: Use of "scare" quotes and the phrase "so-called".
mmeyer@m2.csc.ti.com (Mark Meyer) (08/05/89)
In article <1223@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >* CommonLisp has 5 forms for handling multiple returns, but none of them >allow you to get multiple results from one function call into different >function calls without putting them into variables first. For example, >suppose CommonLisp had a divrem function, and suppose I wanted to >compute x/y*10+log(x%y). I don't see how to do this conveniently without >putting x/y and x%y into temporary variables. Maybe I'm just missing >it. Perhaps you are. Either that or I'm missing your point. Anyway, Common LISP >does< have divrem; it's called truncate. Given x and y, (truncate x y) yields two values q and r such that q*y+r=x, 0<=|r|<|x|, and sign(x)=sign(r). Now, for computing x/y*10+log(x%y), doesn't multiple-value-call do what you want? If we do (defun f (q r) (+ (* q 10) (log r))) then (multiple-value-call f (truncate x y)) computes the original expression for us, taking a pair of values from one function and passing them to another. >-- >-Peter Schachte >pds@quintus.uucp >...!sun!quintus!pds -- Mark Meyer USENET: {ut-sally!im4u,convex!smu,sun!texsun}!ti-csl!mmeyer Texas Instruments, Inc. CSNET : mmeyer@TI-CSL TI's too busy making chips - I had to come up with these opinions myself. Any sufficiently advanced technology is indistinguishable from a rigged demo.
Peter.da.Silva@mamab.FIDONET.ORG (Peter da Silva) (08/06/89)
-- Fidonet: Peter da Silva via 1:363/9 Internet: Peter.da.Silva@mamab.FIDONET.ORG Usenet: ...!peora!rtmvax!libcmp!mamab!Peter.da.Silva
jeff@aiai.uucp (Jeff Dalton) (08/10/89)
In article <1219@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >In article <605@skye.ed.ac.uk> jeff@aiai.UUCP (Jeff Dalton) writes: >>In article <1207@quintus.UUCP>, pds@quintus.UUCP (Peter Schachte) writes: >>> What good are expressions if you can't nest them? >>What good is Prolog? >Prolog is very good (1/2 :-) [for those who don't get the joke, Quintus >sells Prolog systems]. Not only that. Prolog doesn't let you nest extressions. You have to put the values in variables. (So there.) >Prolog avoids this whole problem by using relational, rather than >functional, notation. This means that multiple values may be returned >by a Prolog procedure in exactly the same way as as single value is. I'm not sure this amounts to avoiding the problem. You said that an expression that returned multiple values was "no good for anything but putting on the right side of an assignment" and asked "what good are expressions if you can't nest them?" My point was _not_ that expressions that can't be nested are just as good as expressions that can be nested but rather that they are useful nonetheless. Nonetheless, it's often easier to understand expressions when they're not nested (or not nested very deeply), and the way values are communicated in Prolog is a fairly elegant alternative to assignment.
pds@quintus.UUCP (Peter Schachte) (08/16/89)
In article <713@skye.ed.ac.uk> jeff@aiai.uucp (Jeff Dalton) writes: >My point was _not_ that expressions that can't be nested are just as >good as expressions that can be nested but rather that they are useful >nonetheless. My point was that if you allow some expressions to return multiple values and not allow those to be nested, and still allow the other kind that can be nested but can't return multiple values, you really confuse the idea of an expression. Especially if you can't immediately tell which kind an expression is by looking at it. Better to come up with a semantics for handling a multiple-value expression inside another expression, or as an argument to a function. Or just forget multiple values and pass a pointer to where to put the results. -- -Peter Schachte pds@quintus.uucp ...!sun!quintus!pds
jeff@aiai.uucp (Jeff Dalton) (08/17/89)
In article <1242@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: >My point was that if you allow some expressions to return multiple >values and not allow those to be nested, and still allow the other kind >that can be nested but can't return multiple values, you really confuse >the idea of an expression. Why? >Especially if you can't immediately tell which kind an expression is >by looking at it. When I see "f(x)", I don't know immediately what kind of value it returns. Why should the number of values be such a special case? Of course, it's a less familiar case. But is it much more than that? >Better You think it's better; I think it's not. Now what? >to come up with a semantics for handling a multiple-value >expression inside another expression, or as an argument to a function. Multiple-value-call. Sometimes you have to put the results in variables. What's wrong with that? Use variables all the time anyway. >Or just forget multiple values and pass a pointer to where to put the results. Thus using assignments when they aren't necessary, worrying about aliasing, etc.
mccaugh@s.cs.uiuc.edu (08/22/89)
jeff@aiai.uucp in s.cs.uiuc.edu:comp.lang.misc writes: > In article <1242@quintus.UUCP> pds@quintus.UUCP (Peter Schachte) writes: > >My point was that if you allow some expressions to return multiple > >values and not allow those to be nested, and still allow the other kind > >that can be nested but can't return multiple values, you really confuse > >the idea of an expression. As I read it, the objection raised was that the LIFO behavior of scalar- valued expressions was apparently not being extended to multiple-value ones, to which I would also take exception: that LIFO behavior is expressed in the very syntax of the programming language and appears to have served well for so long; why abort it in the case of multiple-valued expressions? Perhaps the definition of "multiple-valued" needs clarification here: does it refer to data-aggregates?