Multiple return values...

Manu turkeyman at gmail.com
Sun Mar 11 13:41:15 PDT 2012


On 11 March 2012 20:57, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org>wrote:

> I don't know what you mean by "structured type". What I mean by "product
> type" is this: http://en.wikipedia.org/wiki/**Product_type<http://en.wikipedia.org/wiki/Product_type>
>

The first line on that page states my meaning verbatim:
"In programming languages<http://en.wikipedia.org/wiki/Programming_language>
 and type theory <http://en.wikipedia.org/wiki/Type_theory>, a *product* of
*types* is another, compounded, type *in a structure*."

The objectionable part to me is that as soon as it becomes 'structured'
there are additional details in the mix; a defined memory layout and
requirement to adhere by said layout. This allows the coder to do some
things that should be conceptually impossible; take the pointer of the
first item, perform some math, and arrive at another member. This shouldn't
be applicable to a passing in registers ABI.


I don't know how 'major' they'd really work out to be. It's not a
>> paradigm buster. There are some details, but I don't even think it would
>> need to be a breaking change to the language (maybe some very subtle
>> tweaks).
>>
>
> I am convinced you are underestimating. The notion that a function returns
> one typed value is strongly embedded in the language and the standard
> library.


You're probably right, and I don't entirely object to the Tuple solution
(given a nice little bit of sugar), as I've said before (and above), my
concern is that it confuses 2 distinct operations (returning multiple
things, and returning a struct by value), both of which should remain
expressible by the language.


D can return multiple values from a function by putting them in a tuple.
> This approach has many advantages, mostly related to avoiding awkward
> ambiguities (e.g. forwarding the result of a multi-return to a variadic or
> overloaded function). Regarding syntax, I am not convinced by arguments
> predicated on removing "Tuple!" from "Tuple!(int, int)". It's not progress,
> and pouring more magic in tuples that is not available anywhere else does
> not strike me as a good path to go.
>

Well the feature becomes very ugly, clutters the intent with syntax,
requires ugly lines of unpacking syntax, and also asserts to the programmer
that they're exploiting a template library. When I encounter anything like
this in C (*cough* STL), I will immediately lose all trust in the compilers
codegen between compilers (even if the language implements some hacks to
define an efficient ABI for this feature).

None of those are hard reasons against *IF* the codegen does exactly what
it is supposed to. I'll swallow any of those if the codegen works, but it
seems a crying shame to refuse some sugar to interact nicely:

void func(int x)
{
  someStruct s;

  (x, _, s.member, int err) = multiFunc();
  if(err == ...) { ... }
}

Results are written directly where they need to end up, saves lines, it's
clear what I'm doing, I think that's valuable.


Regarding low-level efficiency, the main issue is that structs and function
> argument lists have distinct layouts. Consider:
>
> import std.stdio, std.typecons;
> int a(int b, int c) {
>    return b + c;
> }
> auto foo() {
>    return tuple(1, 1);
> }
> void main() {
>    writeln(a(foo().expand));
> }
>
> An adjustment may be needed from the output of a() to the arguments of
> foo(). (Probably not in this case.) I understand that someone very
> concerned with low-level performance would scrutinize such code carefully.


Wow, I can't imagine how '.expand' could possibly work :) .. that looks
like total magic. A parameter list can be populated by items collected in a
tuple via a property?
If such magic is possible, surely the other stuff is equally possible...

This is an example of where the return-multi ABI mirroring the call args
ABI verbatim would really shine.
In this example, assuming the codegen did it's job, the call to foo() would
return with the arg regs for the call to a() pre-populated. It could just
call straight through without doing anything at all to setup the arguments.
Beautiful!
In C, you could only get this via inline asm.

That said though, I feel this is an extension of the basic multi-return
requirement, perhaps also of significantly lesser importance; chaining
together this way would be rare I would imagine. Of course, if it's easily
possible, no reason against :)



> But I also believe that the same person is willing to forgo a whole
> category of language and library features that don't fulfill a demanding
> performance profile.


I'm not sure what you're getting at here, this is a feature designed to
enhance performance (and also enhance convenience + readability while at it)


In the tight loops of every program I've ever written, I can't recall a
>> time when I haven't had a function that needs to return multiple things,
>> and C/C++ has always forced me into unnecessary memory access to express
>> this (ref parameters). For the first time in language/compiler history,
>> D would finally be able to eliminate the last of the redundant memory
>> accesses in my hottest code in a portable way, at a language/expression
>> level.
>>
>> Are you saying it's impossible, that you don't think it should be done,
>> or it's already solved?
>>
>
> I understand your pledge, and all I'm saying is that it is very difficult
> to implement (Walter and I discussed the matter many times over the years)
> and the feature will benefit only very, very few.
>

If by 'few', you mean the number of programmers that actually write this
code, perhaps... but that's not the full picture.
But all realtime software is ultimately bottlenecked by the performance of
these few super-tight inner loops. More often than not, these exist in
libraries that everyone uses. So the influence of this feature is much
further reaching than the one person who exploited it to improve hottest
part of their code. Those realtime library developers are all acutely aware
of this stuff, will appreciate the feature, and users of said libraries
will all enjoy being able to do a little more with their given system specs.
Average users will also appreciate the code clarity and convenience the
syntax of this feature offers, so they get a direct kickback from this
feature too.


Is it that you don't see the value in it? Is that what I need to
>> convince you of?
>> You haven't argued with any of the other points I raised, which leads me
>> to suspect you either see my points, or you think the entire premise of
>> my rant is wrong... if so, can you show how, and present a solution
>> that's workable within existing constructs that meets my criteria
>> without adding other implicit/logical baggage? Nobody has acknowledged
>>
>> or disputed the majority of my points :/
>>
>
> Your points are understood, but please also understand what exceptional
> circumstances you are invoking. You mention how transferring function
> results costs extra operations. If that cost is significant, that means the
> cost of the function proper is extremely low. So you want to call extremely
> small functions in extremely core loops. The first advice would be "avoid
> calling extremely small functions in extremely core loops"!


In typical circumstances, once you've boiled your problem down, you no
longer have any choice over the parameters of your algorithm design.
Writing this sort of a system tends to be a mechanical process. Once the
algorithm finds maximum efficiency, then low level efficiency comes in, and
this is important. Saying not to call small functions it fallacious.
Obviously I will have already inlined everything I can, but for whatever
reason, there are still a couple of function calls left.
The interesting thing I often find about these sorts of algorithms is I can
often structure the inner loop not to touch memory AT ALL, except for that
one bloody by-ref return parameter, which needlessly reduces my whole
algorithm down to the speed of memory access.


D is a native language, that's it's most attractive feature, and while I
>> appreciate all the high-level correct-ness D offers, it still needs to
>> get the low level right and ideally improve on existing offerings in
>> meaningful ways to motivate new users to switch. This is a big
>> un-checked checkbox for me and my colleagues, and I'd wager any low
>> level realtime programmer out there who has had to struggle with the
>> codegen in their inner loops.. doubly so if they ever work on non-x86
>> systems since the penalties are so much greater.
>>
>
> Are function returning tuples the first thing on your low-level efficiency
> list?


No, virtual-by-default is by a million-fold... that is the biggest low
level disaster in the language hands down! :)

In terms of efficiency, this isn't super high, I just know I'm going to be
swearing the exact the same way I have with in C for years to come, and
breaking out the bloody inline assembler needlessly :(
In terms of convenience however, as said in my OP, I'm finding myself
wishing for this most days.

When taking efficiency & convenience aspects together, and the fact it
ticks a big language feature checkbox, it really does feel worth doing to
me.

I do note that those 2 aspects of this feature are actually separate jobs,
and the sugar one is clearly higher priority, it'll make everyone else
talking here happy too... I just don't want to see it end there. The issue
is that from the efficiency aspect (if implemented via a tuple, and not
special cased), is an ABI breaking change, which should be done sooner than
later to break as little future code as possible.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20120311/ce79b7d4/attachment-0001.html>


More information about the Digitalmars-d mailing list