[comp.lang.c] The need for D-scussion

dsill@NSWC-OAS.arpa (Dave Sill) (03/08/88)

In article <1988Mar6.005553.1284@utzoo.uucp> Henry Spencer <utzoo!henry> writes:
>This sort of wishlist is the wrong way to go about it, assuming that what
>is wanted is a language that will actually be widely implemented and widely
>used.  If that is to happen, the new language must be either (a) virtually
>completely upward-compatible with C and significantly better, or (b) *LOTS*
>better than C in at least one area.

Personally, I think (b) is the way to go.  Sure, upward compatibility
would be a major selling point, but it carries with it the dread
Backward Compatibility.  It's hard to leap when you only use one leg.

>Most of the things on this wishlist
>are incompatible but unimportant.  (Not trivial, necessarily, and in the
>abstract I agree with many of them, but that isn't the issue.)  Unless some
>truly major improvement can be had somewhere else, a language designed from
>this wishlist may receive critical acclaim but will never be popular.

I disagree that the items on the wishlist are unimportant.  True
multidimensional arrays, better bit-level data support, a general
aggregate constructor mechanism, a better declaration syntax, and a
more general macro facility are *major* improvements.  Also, although
many of the other items on the wishlist are minor, their combined
effect would be large.

>Remember also that the competition is not just C.  C++ is both upward
>compatible *and* a major improvement in certain areas.

This is a good point.  I think more C++ features, if not all, should
be considered for inclusion in D.

>I used to be very enthusiastic about designing better languages.  But I
>can no longer work up much enthusiasm for lost causes.  I could easily
>design a language that would be noticeably better than C.  However, I don't
>know how to design a language that is *enough* better than C++ for it to
>be worth the trouble.

I'm sorry, but I just don't think C++ is so good that a *much* better
language is not possible.  The upward compatibility that makes C++ so
attractive initially will probably be its downfall.  Most of its
weaknesses and limitations are a result of backward compatibility.
Don't get me wrong, I think C is an excellent language; and C++ is
even better.  But I also think a much better language based on C/C++
but with some of the weaknesses removed and better support for current
and projected state-of-the-art environments is not only possible, but
worthy of pursuit.

If nothing else, this discussion serves to highlight the imperfections
of C/C++ and to stimulate thought on what could be done better.

>Those who do not understand Unix are |  Henry Spencer @ U of Toronto Zoology
>condemned to reinvent it, poorly.    |{allegra,ihnp4,decvax,utai}!utzoo!henry

Similarly, those who understand C and its limits are less likely to
propagate them.

=========
The opinions expressed above are mine.

"Don't let existing sub-optimal solutions cloud your vision."
					-- Fred Fish

henry@utzoo.uucp (Henry Spencer) (03/12/88)

> >Most of the things on this wishlist
> >are incompatible but unimportant.  (Not trivial, necessarily...
> 
> I disagree that the items on the wishlist are unimportant.  True
> multidimensional arrays, better bit-level data support, a general
> aggregate constructor mechanism, a better declaration syntax, and a
> more general macro facility are *major* improvements...

Au contraire.  All they do is reduce the clumsiness of doing those
things in C.  While that is certainly useful, it is not going to sell
people on a new and incompatible language.  To do that, you need to
show an order-of-magnitude improvement -- overall, not just in a few
narrow areas.  The things you have listed above are all just icing on the
cake; they would *help* sell a language that was already a real contender
for other reasons, but by themselves they aren't enough.  (Analogously,
C++ includes a number of useful, nice small improvements on C, but it
would not be worth the trouble without its abstract data types and
object-oriented type system.)

> I'm sorry, but I just don't think C++ is so good that a *much* better
> language is not possible...

Oh, I agree it's *possible*; my point is that I don't know how to do it.
A little better, yes, that I could do.  Fifteen years of active interest
in language design, and considerable private work on ideas about the
ideal programming language, has given me more than enough thoughts on
ways to improve C.  But most of them would be, indeed, just icing.  My
conclusion, after a lot of thought, is that it doesn't matter how much
icing you put on if there's no cake underneath.  I don't know how to
get the order-of-magnitude improvement needed to make lots of converts.
That requires at least one quantum leap upward in the power of the language,
not just incremental improvements and smoothing down the rough edges.
-- 
Those who do not understand Unix are |  Henry Spencer @ U of Toronto Zoology
condemned to reinvent it, poorly.    | {allegra,ihnp4,decvax,utai}!utzoo!henry

tada@athena.mit.edu (Michael Zehr) (03/14/88)

Here's another one for the D wishlist:

a function the returns both a % b and a / b at the same time.

I know I've had to call those routines with the same values consecutively
in code that needed to run fast, and if i understand the library right, they
both compute the pair of values, but a different one is returned.  Why not
give the option of getting them both at the same time?

(Of course, this requires returning multiple values ...)
-------
michael j zehr
"My opinions are my own ... as is my spelling."

wesommer@athena.mit.edu (William Sommerfeld) (03/14/88)

In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
>a function the returns both a % b and a / b at the same time.
>
>I know I've had to call those routines with the same values consecutively
>in code that needed to run fast, and if i understand the library right,

What library?  If you've got a CISC processor with integer divide in
microcode (such as the VAX), divide is a single instruction.  Modulo
might be, too.

On just about any reasonable processor, if you're computing someething
% or / a power of two, you can turn the operation into an `and' (with
(2**n)-1) or `shift right' (by n), respectively, which _is_ in
hardware

>they
>both compute the pair of values, but a different one is returned.  Why not
>give the option of getting them both at the same time?

Well, the VAX has this `ediv' instruction which does both a divide and
a modulo..  A good compiler would notice the `common subexpression':

	c = a / b;
	d = a % b;

and generate the one hairy instruction or function call.  I think
there's some support in the GNU C compiler for this, but it's turned
off on the VAX because `ediv' is apparantly a real turkey of an
instruction.

					- Bill


	

gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/14/88)

In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
>a function the returns both a % b and a / b at the same time.

This is the first time I recall in the "D" discussion that an useful
genuine language feature has been proposed.  All you need now is a
suitable notation..

sjs@spectral.ctt.bellcore.com (Stan Switzer) (03/15/88)

In article <7451@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
> In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
> >a function the returns both a % b and a / b at the same time.
> 
> This is the first time I recall in the "D" discussion that an useful
> genuine language feature has been proposed.  All you need now is a
> suitable notation..

How about:
	q = a/b, r = a%b;

It works, it is clean, and any respectable compiler will do the
right thing.

tim@amdcad.AMD.COM (Tim Olson) (03/15/88)

In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
| 
| Here's another one for the D wishlist:
| 
| a function the returns both a % b and a / b at the same time.
| 
| I know I've had to call those routines with the same values consecutively
| in code that needed to run fast, and if i understand the library right, they
| both compute the pair of values, but a different one is returned.  Why not
| give the option of getting them both at the same time?

If you have a good compiler, you can already get this.  The low-level
divide function for the MetaWare - Am29000 C compiler returns the /
value in the specified register, and returns the % value in the standard
function return-value register.  Therefore, in code like

	while (n > 0) {
		buf[i++] = (n%10) + '0';
		n /= 10;
	}

only one call to the low-level divide routine is made.

	-- Tim Olson
	Advanced Micro Devices
	(tim@amdcad.amd.com)

henry@utzoo.uucp (Henry Spencer) (03/15/88)

> a function the returns both a % b and a / b at the same time.

ANSI has beaten you to it.  See section 4.10.6.2 in the current X3J11 draft,
which defines the "div" function, returning a struct containing both
quotient and remainder.  (They chose the wrong definition of division, but
I suppose one can't have everything...)

This is probably the best approach to the issue, since not all machines do
in fact efficiently compute both in the same operation.  (The VAX, for
example, has fast instructions that give you either, and a [reportedly]
rather slow instruction that gives you both.)  Better to supply both only
when asked.
-- 
Those who do not understand Unix are |  Henry Spencer @ U of Toronto Zoology
condemned to reinvent it, poorly.    | {allegra,ihnp4,decvax,utai}!utzoo!henry

karl@haddock.ISC.COM (Karl Heuer) (03/15/88)

In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
>a function the returns both a % b and a / b at the same time.

ANSI C has two flavors of this, in function form: div() and ldiv().  The
return value is a structure, which makes it a bit awkward to use if you want
both results.  Also, unlike the / and % operators, the result is well-defined
(guaranteed vax-like) for negative numbers, so it is not necessarily identical
to the pair {a/b, a%b}.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

karl@haddock.ISC.COM (Karl Heuer) (03/15/88)

In article <3734@bloom-beacon.MIT.EDU> wesommer@athena.mit.edu (William Sommerfeld) writes:
>On just about any reasonable processor, if you're computing someething
>% or / a power of two, you can turn the operation into an `and' (with
>(2**n)-1) or `shift right' (by n), respectively, which _is_ in hardware

Assuming a two's complement representation and that divide rounds toward zero
(as on a VAX, the usual standard of a "reasonable processor"), your suggested
optimization is not valid if the numerator could be negative.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

rk9005@cca.ucsf.edu (Roland McGrath) (03/15/88)

["The need for D-scussion (was Re: D Wishlist)"] - gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>):
} In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
} >a function the returns both a % b and a / b at the same time.
} 
} This is the first time I recall in the "D" discussion that an useful
} genuine language feature has been proposed.  All you need now is a
} suitable notation..

How about:
	div_t x = div(a, b);
?

Why add an operator when there's a standard out there requiring
a simple function to do it?
-- 
	Roland McGrath
ARPA: roland@rtsg.lbl.gov roland@lbl-rtsg.arpa
UUCP: ...!ucbvax!lbl-rtsg.arpa!roland

karl@haddock.ISC.COM (Karl Heuer) (03/16/88)

In article <6128@bellcore.bellcore.com> sjs@spectral.UUCP (Stan Switzer) writes:
>>>a function the returns both a % b and a / b at the same time.
>
>How about:   q = a/b, r = a%b;
>It works, it is clean, and any respectable compiler will do the right thing.

That's fine if you actually have variables a and b, but it's not quite that
simple if they are more general expressions.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

bs@linus.UUCP (Robert D. Silverman) (03/17/88)

In article <7451@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <3732@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael Zehr) writes:
>>a function the returns both a % b and a / b at the same time.
>
>This is the first time I recall in the "D" discussion that an useful
>genuine language feature has been proposed.  All you need now is a
>suitable notation..


I am a computational number theorist who writes a LOT of code requiring
that:

A*B/C
A*B  % C

be computed where A,B,C are all 30 bit quantities.

The difficulty is that even though the algorithm guarantees that the results
of the computations fit in 30 bits, the product A*B may not. I therefore
need to make extensive use of any 'emul' and 'ediv' facilities that a
computer may have. I have written code which spends 25% of its time JUST
doing the above computations (and takes 100's of hours to run).
Since there is no 'long long' in C I am forced to write an assember routine 
which does the computations as follows:

remainder = mod_mult(A,B,C,&quotient)

or the equivalent depending on circumstances. It must first emul A and B
then ediv by C. Since most machines which have ediv return the quotient and
remainder in two different registers no extra work is required to get the
remainder after doing the division.

Machines which have such instructions should, In my opinion, generate 
emul/ediv code whenever it sees A*B/C and only return a run-time arithmetic
overflow if the quotient doesn't fit in the appropriate word-size. There
should at least be some compiler switch which allows this option. What good
are emul and ediv if you can't reach them from higher level languages?
Especially on a VAX if you have to call an assembler routine to perform
the above, the subroutine call itself can take about as much time as the
calculations. A calls on the VAX/780 with 4 arguments takes ~ 17usec.
The ediv takes 11.9 and the emul takes 6.8usec. UGLY.
 
There is some need (although I will concede not a lot) for multi-precision
arithmetic support. Modern languages are woefully inadequate for the task.


Bob Silverman

ok@quintus.UUCP (Richard A. O'Keefe) (03/18/88)

In article <27143@linus.UUCP>, bs@linus.UUCP (Robert D. Silverman) writes:
> I am a computational number theorist who writes a LOT of code requiring that:
> A*B/C
> A*B  % C
> be computed where A,B,C are all 30 bit quantities.

Here is something I knocked together for a Sun-3.  It relies on the "inline"
processor which comes with SUN's compilers.  Some other versions of UNIX have
it, but I don't know which.  I decided to post this to comp.lang.c because
this approach seems to be far the cleanest way of getting assembly code into
a C program.  You write an ordinary function call, which can be checked by
lint just like any other function call.  The compiler replaces such function
calls by the assembly code of your choice *before* putting the intermediate
code through the -O phase, so parts of the code you wrote can actually be
fused with parts the compiler wrote.

Of course this sort of thing is machine dependent, but the really nice thing
is that it doesn't make the code that USES inlined code machine dependent.
You can provide ordinary C code for such functions if you want to (great for
debugging).

Anyway, here's the code.  THIS CODE IS PROVIDED FREE TO ILLUSTRATE A
TECHNIQUE.  I MAKE NO CLAIM THAT ANY PART OF IT FUNCTIONS CORRECTLY.

#!/bin/sh
cat >muldivrem.il <<'------ EOF ------'
|   File   : muldivrem.il
|   Author : Richard A. O'Keefe
|   Defines: in-line expansion for mul_div_rem

|   The idea is that we want a function
|	int mul_div_rem(A, B, C, D, Q, R)
|	    /* 32-bit */ int A, B, C, D;
|	    /* 32-bit */ int *Q, *R;
|   which does
|	long long int T = A*B+C;
|	if (D == 0 || T/D overflow) {
|	    *Q = high bits of T, *R = low bits of T;
|	    return 0;
|	} else {
|	    *Q = T/D, *R = T%D;
|	    return non-zero;
|	}
|   The fact that we get the high and low bits of T in the case of an
|   overflow is due to the definition of the M68020 divs.l instruction,
|   which doesn't change the destination in this case.  More generally,
|   we might recompute the result.  mul_div_rem with D=0 is useful for
|   getting A*B+C without doing a division.
|   The point of this file is to have the operation realised by in-line
|   assembly code, rather than by a C function call with all its overhead.
|
|    Assumptions:
|	registers d0, d1, d2, and a0 are free.

	.inline	_mul_div_rem,24
	movl	sp@+,d2
	mulsl	sp@+,d1:d2
	clrl	d0
	addl	sp@+,d2
	addxl	d0,d1
	movl	sp@+,d0
	blts	1f		| if D is zero, don't do the division
	divsl	d0,d1:d2
	bvcs	1f		| if quotient overflow happened
	clrl	d0		| set the result to 0
    1:	movl	sp@+,a0
	movl	d2,a0@
	movl	sp@+,a0
	movl	d1,a0@
	.end
------ EOF ------
ls -l muldivrem.il
cat >mdrtest.c <<'------ EOF ------'
/*  File   : mdrtest.c
    Author : Richard A. O'Keefe
    Purpose: Test mul_div_rem
*/

extern int mul_div_rem();

main()
    {
	int a, b, c, d, q, r;

	while ((printf("a,b,c,d:= "), scanf("%d%d%d%d", &a,&b,&c,&d)) == 4) {
	    a = mul_div_rem(a, b, c, d, &q, &r);
	    printf("a=%d, q=%d, r=%d\n", a, q, r);
	}
    }

------ EOF ------
ls -l mdrtest.c
cat >mdrscript <<'------ EOF ------'
#!/bin/sh
#   File   : mdrscript
#   Author : Richard A. O'Keefe
#   Purpose: compile mdrtest

cc -o mdrtest -O mdrtest.c muldivrem.il

------ EOF ------
ls -l mdrscript

gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/19/88)

In article <6128@bellcore.bellcore.com> sjs@spectral.UUCP (Stan Switzer) writes:
>How about:
>	q = a/b, r = a%b;

Nice try, but no cigar.  It works fine for this simple example, but
what if a and b are replaced by more complex expressions, perhaps
involving side-effects?  This is the same situation that justifies
the presence of the op= operators in the language.

karl@haddock.ISC.COM (Karl Heuer) (03/21/88)

In article <27143@linus.UUCP> bs@gauss.UUCP (Robert D. Silverman) writes:
>I am a computational number theorist who writes a LOT of code requiring that:
>A*B/C [and] A*B%C be computed where A,B,C are all 30 bit quantities.  The
>difficulty is that even though the algorithm guarantees that the results of
>the computations fit in 30 bits, the product A*B may not. I therefore need to
>make extensive use of any 'emul' and 'ediv' facilities that a computer may
>have. ... Machines which have such instructions should, In my opinion,
>generate emul/ediv code whenever it sees A*B/C and only return a run-time
>arithmetic overflow if the quotient doesn't fit in the appropriate word-size.

Such a special case would be a wart in the language.  The "correct" way to do
this is to provide an "lmuldiv" function which has the desired semantics (I'd
have it return an ldiv_t structure, since it already exists in ANSI C), and
encourage vendors to give their compilers the ability to inline it (along with
other trivial functions, such as abs()).

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

cik@l.cc.purdue.edu (Herman Rubin) (03/22/88)

In article <3073@haddock.ISC.COM>, karl@haddock.ISC.COM (Karl Heuer) writes:
> In article <27143@linus.UUCP> bs@gauss.UUCP (Robert D. Silverman) writes:
> >I am a computational number theorist who writes a LOT of code requiring that:
> >A*B/C [and] A*B%C be computed where A,B,C are all 30 bit quantities.  The
> >difficulty is that even though the algorithm guarantees that the results of
> >the computations fit in 30 bits, the product A*B may not. I therefore need to
> >make extensive use of any 'emul' and 'ediv' facilities that a computer may
> >have. ... Machines which have such instructions should, In my opinion,
> >generate emul/ediv code whenever it sees A*B/C and only return a run-time
> >arithmetic overflow if the quotient doesn't fit in the appropriate word-size.

I think Bob has oversimplified slightly and not asked for enough.  What I would
like to see is something like

	u,v = a*b///c

(or however he would like to write it; maybe eventually it would become
standardized, but it is not now).  What a reasonable language needs is the
ability of the user to define types and operations at will.  C++ allows the
introduction of types in some, but not all situations; it does not allow the
introduction of operators.

> Such a special case would be a wart in the language.  The "correct" way to do
> this is to provide an "lmuldiv" function which has the desired semantics (I'd
> have it return an ldiv_t structure, since it already exists in ANSI C), and
> encourage vendors to give their compilers the ability to inline it (along with
> other trivial functions, such as abs()).

This is precisely what has made it difficult for mathematicians, and all who
understand the computer, to make reasonable use of it.  To follow the desire
of the Mikado to "make the punishment fit the crime," I would sentence all 
those who would deny the right to use infix notation to others the right to
use it for themselves.  It is the use of prefix rather than infix notation
which makes assembler programming such a chore in 99% of the assemblers.

Furthermore, returning a list of values for a function is far different from
returning a structure.  In the case above, it is likely that everything
should be in registers.  And it takes no longer to produce the efficient
code with a reasonable language than it does to produce the less efficient
code with the "warts" removed.  In fact, it takes much less time to write
a reasonable notation.  It takes less time to read someone's reasonable infix
notation, even if that is not the way I would write it, than prefix notation,
if I have any idea of what is being done.

As long as programmers are taught to think in terms of a language, rather
than the machine, it will be difficult to get vendors to allow the forcing
of inline.

-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (ARPA or UUCP) or hrubin@purccvm.bitnet

chris@mimsy.UUCP (Chris Torek) (03/23/88)

In article <719@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>This is precisely what has made it difficult for mathematicians, and all who
>understand the computer, to make reasonable use of it.  To follow the desire
>of the Mikado to "make the punishment fit the crime," I would sentence all 
>those who would deny the right to use infix notation to others the right to
>use it for themselves.  It is the use of prefix rather than infix notation
>which makes assembler programming such a chore in 99% of the assemblers.

You must really hate Lisp, and Forth, and RPN calculators, and . . . .

>Furthermore, returning a list of values for a function is far different from
>returning a structure.

If so, the compiler should be improved.  There is no theoretical difference
(unless you allow variable length lists, but that is not what people were
talking about).

>As long as programmers are taught to think in terms of a language, rather
>than the machine, it will be difficult to get vendors to allow the forcing
>of inline.

As long as programmers are taught to think in terms of a machine,
rather than the language, it will be difficult to get portable code
that can be moved to that 1 teraflop computer that will come out
tomorrow, then to the 10 teraflop computer that will come out the day
after, and then to the 100 teraflop computer that will come out a week
from Monday.

(When someone finally builds the Ultimate Computer, the one that
uses infinite parallelism and quantum effects to achieve the fastest
operation that can ever exist, *then* I will give in on this argument.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

shebs%defun.utah.edu.uucp@utah-cs.UUCP (Stanley T. Shebs) (03/23/88)

In article <719@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:

>[...] It is the use of prefix rather than infix notation
>which makes assembler programming such a chore in 99% of the assemblers.

Gee, and I thought it was the explicit management of registers and memory
that made assembly programming such a chore!  CAL (Cray Assembly Language)
is infix, but I hadn't noticed that it made much difference to anybody.

In case anybody hadn't realized it, this is the same Herman Rubin that's
been calling for "portable assembly languages" for years.  The suggestion
that such a desire is self-contradictory doesn't seem to bother him.
The suggestion that he should try doing this himself is met with a request
for money upfront, with no concrete evidence that the activity would be
useful.  He still doesn't seem to have consulted the literature - reading
a CACM from, say, 1965 is very educational, particularly if you still have
dreams of a "high-level language that allows the programmer to exploit
special machine instructions".  A language like C didn't get designed in
a vacuum, you know - "systems programming languages" was an intensely
studied area in the late 60s, and C just happened to be the winner of those
long-ago battles...

							stan shebs
							shebs@cs.utah.edu

karl@haddock.ISC.COM (Karl Heuer) (03/26/88)

In article <10763@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>In article <719@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>>Furthermore, returning a list of values for a function is far different from
>>returning a structure.
>
>If so, the compiler should be improved.  There is no theoretical difference.

Or perhaps the language should be improved.  In C, the div() function returns
a struct.  If I want to add the squares of the two components, I have to write
either
  qr = div(x, y);
  z = qr.quot * qr.quot + qr.rem * qr.rem;
or use a function
  int f(struct div_t qr) { return (qr.quot * qr.quot + qr.rem * qr.rem); }
  z = f(div(x, y));
The former requires a throwaway variable; the latter, a throwaway function.

In Lisp, you're still returning an aggregate.  Things are a little better,
because you can tighten the scope:
  (setq z
        ((lambda (qr) (+ (* (car qr) (car qr)) (* (cdr qr) (cdr qr))))
         (div x y)))

In Forth, you can actually return multiple values (as opposed to returning a
single value of aggregate type):
  x @ y @ div  dup *  swap dup *  +  z !

There's a certain elegance here which is lacking in the first two.  The qr
temporary has been absorbed by the syntax of the language, in exactly the same
manner as the temporary results of the "*" operator in all three examples.

I don't know if this is what Herman was talking about, but in this sense the
current C model is lacking.  Whether this could be "fixed" in a C-like
language is another question.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

cik@l.cc.purdue.edu (Herman Rubin) (03/27/88)

In article <3177@haddock.ISC.COM>, karl@haddock.ISC.COM (Karl Heuer) writes:
> In article <10763@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
> >In article <719@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
> >>Furthermore, returning a list of values for a function is far different from
> >>returning a structure.
> >
> >If so, the compiler should be improved.  There is no theoretical difference.

If so, there is at least confusion.  As I read K&R, a struct is a memory
assignment.  In some cases the memory assignment can be transferred to the
register file.  A list consists of items each of which has its own location.
It does not convey the impression that the locations of the items in the list
have any relation to each other.  One item can be a register, another a byte
local to the program, another an external float, we may possibly have both
local and external arrays, etc.

Generalizing the K&R struct cannot solve the problem.  If an external is to be
in the object, we have the problem of multiple definition.  Furthermore, if I
use the same type of list several times, with different arguments each time, it
is merely necessary to type in the relative arguments.  If we want to use the 
x.y struct notation and have the list expanded, we can easily do it with a
#define statement, but the other way is hard to do.  We are dealing with
different constructs.

> Or perhaps the language should be improved.  In C, the div() function returns
> a struct.   .....

> In Lisp, you're still returning an aggregate.  Things are a little better,
> because you can tighten the scope:
>   (setq z
>         ((lambda (qr) (+ (* (car qr) (car qr)) (* (cdr qr) (cdr qr))))
>          (div x y)))
> 
> In Forth, you can actually return multiple values (as opposed to returning a
> single value of aggregate type):
>   x @ y @ div  dup *  swap dup *  +  z !

In Lisp, the language certainly suggests that at some time there is a real
machine object called qr.  The way I am proposing the list, this is never
more than a virtual object, and does not need a name.  However, the user
assigns names to the elements of the list, and refers to them, rather than
having to write (car qr) and (cdr qr).  I am not saying that the user must
not set up the suggested notation, rather that he need not.

The Forth example requires that the stack exist.  I think that this may
make it extremely difficult for a compiler to "do what is natural."  It is
likely that the "natural" thing to do is far more efficient also.

> There's a certain elegance here which is lacking in the first two.  The qr
> temporary has been absorbed by the syntax of the language, in exactly the same
> manner as the temporary results of the "*" operator in all three examples.
> 
> I don't know if this is what Herman was talking about, but in this sense the
> current C model is lacking.  Whether this could be "fixed" in a C-like
> language is another question.

Too much elegance, and not enough simplicity.  Both versions are unnecessarily
complicated.  From the standpoint of a computer language, how can there be any
difficulty in allowing operators or procedures to produce lists?  I doubt that
the notation

	q,r = a _div_ b

will confuse a human reader of this article.  Whether we have, by ignoring 
this problem in the past, messed up our current compilers so that it is
necessary to do a complete rewriting rather than a fix is another matter.

> Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint


-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (ARPA or UUCP) or hrubin@purccvm.bitnet