[comp.arch] 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

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

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.