[comp.lang.misc] Language Tenets

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, &quotient, &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, &quotient, &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?