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
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
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
nick@lfcs.ed.ac.uk (Nick Rothwell) (07/18/89)
In article <84306@ti-csl.csc.ti.com>, gateley@m2 (John Gateley) writes: >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. That's my point (I mentioned garbage collection). To treat multiple values in a proper uniform way, you have to be able to pass them around as objects, and so garbage-collect them when they are no longer accessible. This avoids these problems. Your problem is perhaps one of your perception of the implementation - I wasn't thinking about stacks at all... >John 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.
dlw@odi.com (Dan Weinreb) (07/22/89)
From: gateley@m2.csc.ti.com (John Gateley) Newsgroups: comp.arch,comp.lang.misc 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. I don't know what you mean by unpackaging them. The typical use I'd make of a divrem function, in the high-level terms in which I think, is usually something like this: (1) compute the numerator (2) compute the denominator (3) perform divrem (4) give a name to the quotient and remainder (5) proceed with my computation, now that I have the quotient and remainder For example, (multiple-value-bind (n-blocks offset-within-block) (divrem (get-total-size-in-units) size-of-block-in-units) (format t "There are %d blocks, with %d units left over%n" n-blocks offset-within-block) (dotimes (i n-blocks) (process-one-block)) ... and so on ... ) Back before we had multiple values, we had to create bogus structures; divrem would return one, then we'd need this awkward code to extract the values from the structure. To add insult to injury, the structure was then garbage and contributed eventually to GC costs. The great thing about this style is that there isn't any unpackaging. There isn't any synthetic, bogus, distracting object whose only purpose is to hold the two values. Such an object is bogus because it does not model any concept at the level of the application; it's only there for programming language purposes, not because it is a useful expression of what the program is about. (Usually, you notice this when you try to figure out what its name is. Usually there isn't any good name and you have to make up something rather non-mnemonic and awkward.) I used the programming style illustrated above in Zetalisp and Common Lisp for about ten years, and in my experience and opinion, it's quite useful and readable.
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.