December 01, 2003
"Berin Loritsch" <bloritsch@d-haven.org> wrote in message
>
> Honestly, I have not even thought about a feature like this...
Practlically
> speaking, how would you call such a beast in the client code?
>
> (foo, bar) = calculateIt( x, y, z, t );
>
> and if we wanted to explicitly ignore a value:
>
> (,bar) = calculateIt( x, y, z, t );
>
> ???
>

Perhaps, but presumably, you could overload a function based on its return parameter list as well as its argument list.  For example

class MyClass {
    int func( int x, int y, int z) {
        int result ;
        /* do stuff */
        return result ;
    }
    (int, float) func( int x, int y, int z) {
        float bar ;
        int ibar = func(x, y, z) ;
        /* do stuff */
        return ( ibar, bar ) ;
    }
}

Ignoring return parameters may be something necessary however.  I would say that they are analogous to default arguments (like C++).  Of course it would be difficult to determine which return value one may wish to ignore,  so requiring that all ignorable return parameters appear at the end of the parameter list is a bit restrictive.

An alternative syntax for ignoring return parameters would be using the null keyword (is 'null' a key word in D?).  For example,

MyClass me = new MyClass ;
int ibar;
float bar ;
(ibar, null) = me.func(1.0, 2.0, 3.0) ;
/* or */
(null, bar) = me.func(1.0, 2.0, 3.0) ;
/* or */
(ibar, bar) = me.func(1.0, 2.0, 3.0) ;

Another alternative is to use named parameters.  (I saw some discussion
about this.)  For example

class MyClass {
    (int a, float b) func( int x, int y, int z) {
        float bar ;
        int ibar  ;
        /* do stuff */
        return ( ibar, bar ) ;
    }
}

MyClass me = new MyClass ;
int ibar;
float bar ;
(a:ibar) = me.func(1.0, 2.0, 3.0) ;
/* or */
(b:bar) = me.func(1.0, 2.0, 3.0) ;
/* or */
(a:ibar, b:bar) = me.func(1.0, 2.0, 3.0) ;
/* or just */
(ibar, bar) = me.func(1.0, 2.0, 3.0) ;

This whole discussion however raises a larger issue.  That is, what we are really discussing is expanding and generalizing what an lvalue and rvalue can and should be.  For example if an lvalue was allowed to take the form of a parameter list, then the multiple return would be almost free.  For example, a parameter list lvalue (and rvalue) would result in statements such as:

(x, y) = (r*cos(theta), r*sin(theta)) ;

/* which is more compact than.. */
x = r*cos(theta) ;
y = r*sin(theta)  ;

which would function equivalent to the following function call,

(x, y) = toCartesian( r, theta) ;

/* which is more elegant (IMO) than  */

toCartesian( r, theta, &x, &y) ;

Mark.



December 01, 2003
Mark J. Brudnak wrote:

> "Berin Loritsch" <bloritsch@d-haven.org> wrote in message
> 
>>Honestly, I have not even thought about a feature like this...
> 
> Practlically
> 
>>speaking, how would you call such a beast in the client code?
>>
>>(foo, bar) = calculateIt( x, y, z, t );
>>
>>and if we wanted to explicitly ignore a value:
>>
>>(,bar) = calculateIt( x, y, z, t );
>>
>>???
>>
> 
> 
> Perhaps, but presumably, you could overload a function based on its return
> parameter list as well as its argument list.  For example
> 
> class MyClass {
>     int func( int x, int y, int z) {
>         int result ;
>         /* do stuff */
>         return result ;
>     }
>     (int, float) func( int x, int y, int z) {
>         float bar ;
>         int ibar = func(x, y, z) ;
>         /* do stuff */
>         return ( ibar, bar ) ;
>     }
> }
> 

... Runs away screamming ... NOOOOOO!!!!!

When the only difference between two functions is the return parameter, you are
inviting disaster.  How can you guarantee in what context which of the two
methods are called?

I personally would not want to maintain an application which uses such a
construct, and it is explicitly forbidden in other languages.  I think for a
good reasons.

It's a stretch for me just to accept multiple return values--when you throw in
differing methods based on return type, things get really messy really fast.
Please don't do such a thing.

December 01, 2003
"Berin Loritsch" <bloritsch@d-haven.org> wrote >
>
> Honestly, I have not even thought about a feature like this...
Practlically
> speaking, how would you call such a beast in the client code?
>
> (foo, bar) = calculateIt( x, y, z, t );
>
> and if we wanted to explicitly ignore a value:
>
> (,bar) = calculateIt( x, y, z, t );
>
> ???
>

Perhaps you could overload a function based on its return parameter list as well as its argument list.  For example

class MyClass {
    int func( int x, int y, int z) {
        int result ;
        /* do stuff */
        return result ;
    }
    (int, float) func( int x, int y, int z) {
        float bar ;
        int ibar = func(x, y, z) ;
        /* do stuff */
        return ( ibar, bar ) ;
    }
}

Ignoring return parameters may be something necessary however.  I would say that they are analogous to default arguments (like C++).  Of course it would be difficult to determine which return value one may wish to ignore,  so requiring that all ignorable return parameters appear at the end of the parameter list is a bit restrictive.

An alternative syntax for ignoring return parameters would be using the null keyword (is 'null' a key word in D?).  For example,

MyClass me = new MyClass ;
int ibar;
float bar ;
(ibar, null) = me.func(1.0, 2.0, 3.0) ;
/* or */
(null, bar) = me.func(1.0, 2.0, 3.0) ;
/* or */
(ibar, bar) = me.func(1.0, 2.0, 3.0) ;

Another alternative is to use named parameters.  (I saw some discussion
about this.)  For example

class MyClass {
    (int a, float b) func( int x, int y, int z) {
        float bar ;
        int ibar  ;
        /* do stuff */
        return ( ibar, bar ) ;
    }
}

MyClass me = new MyClass ;
int ibar;
float bar ;
(a:ibar) = me.func(1.0, 2.0, 3.0) ;
/* or */
(b:bar) = me.func(1.0, 2.0, 3.0) ;
/* or */
(a:ibar, b:bar) = me.func(1.0, 2.0, 3.0) ;
/* or just */
(ibar, bar) = me.func(1.0, 2.0, 3.0) ;

This whole discussion however raises a larger issue.  That is, what we are really discussing is expanding and generalizing what an lvalue and rvalue can and should be.  For example if an lvalue was allowed to take the form of a parameter list, then the multiple return would be almost free.  For example, a parameter list lvalue (and rvalue) would result in statements such as:

(x, y) = (r*cos(theta), r*sin(theta)) ;

/* which is more compact than.. */
x = r*cos(theta) ;
y = r*sin(theta)  ;

which would function equivalent to the following function call,

(x, y) = toCartesian( r, theta) ;

/* which is more elegant (IMO) than  */

toCartesian( r, theta, &x, &y) ;

Mark.




December 01, 2003
Mark J. Brudnak wrote:

> "Berin Loritsch" <bloritsch@d-haven.org> wrote >
> 
>>Honestly, I have not even thought about a feature like this...
> 
> Practlically
> 
>>speaking, how would you call such a beast in the client code?
>>
>>(foo, bar) = calculateIt( x, y, z, t );
>>
>>and if we wanted to explicitly ignore a value:
>>
>>(,bar) = calculateIt( x, y, z, t );
>>
>>???
>>
> 
> 
> Perhaps you could overload a function based on its return
> parameter list as well as its argument list.  For example
> 
> class MyClass {
>     int func( int x, int y, int z) {
>         int result ;
>         /* do stuff */
>         return result ;
>     }
>     (int, float) func( int x, int y, int z) {
>         float bar ;
>         int ibar = func(x, y, z) ;
>         /* do stuff */
>         return ( ibar, bar ) ;
>     }
> }

<snip/>

NOOO!!!  Please see my previous posting on why not.  It is a debug nightmare
waiting to happen.

December 01, 2003
In article <bqffkg$qin$1@digitaldaemon.com>, Mark J. Brudnak says...
>
>The syntax should be:
>
>(int, float, double) funct( char[] foo, int bar) {
>    int result1 ;
>    float result2;
>    double result3 ;
>
>    /* do stuff */
>
>    return (result1, result2, result3) ;
>}
>
>This would ofcourse impose a special meaning on the parenthesis and comma for the return statement.  I assume that D did away with the C-"comma operator" so there would be no ambiguity in the syntax.  This amounts to nothing more than a compiler generated struct used only for the passing of parameters on return.
>
>mark.
>
>
>"Felix" <Felix_member@pathlink.com> wrote in message news:bqesvv$310k$1@digitaldaemon.com...
>> Just a thought: I work in Matlab so I use the multiple return parameters. However, there are a few issues:
>>
>> -concerning the "C"/"D" return values (i.e. using "return x;" would have
>to be
>> chnaged too, to accept multiple parameters). else, using a more
>"Pascal"-like
>> approach, i.e. returning the output parameters with the last value they
>have
>> before quiting the fcn. will simply change some fondamental concepts. -in Matlab, one feature missing is the non-reference parameters transfer.
>This
>> means that, for each function affecting x you must write x=domsmthngto(x).
>IMO,
>> this should be avoided in "D". Is not very cool.
>>
>>
>> Felix
>>
>>
>>
>>
>> In article <bq9e98$1f9f$1@digitaldaemon.com>, Matthew Wilson says...
>> >
>> >
>> >"Charles Sanders" <sanders-consulting@comcast.net> wrote in message news:bq8vs3$qos$1@digitaldaemon.com...
>> >> > > 1) Multiple return values (like Matlab)
>> >>
>> >> Boost has accomplished these 'tuples' through some template magic,
>perhaps
>> >> the same can be done with D, although i agree I would LOVE to see a
>> >language
>> >> that implemented this.
>> >
>> >I'd rather see it in the language, but if it's not I've no doubt it'll
>find
>> >its way in when the templates get a serious shake.
>> >
>> >
>> >
>>
>>
>
>

You can already do this.  You can return a struct I in C and C++.
A struct can contain more than one value and encapsulate more than one return
of types.  It is better to return a struct of values than have a multiple return
for functions.  This is how it was intended to return multiple types.

A link for gcc is:  http://gcc.gnu.org/ml/gcc/1998-11/msg01087.html

Here's an example from that link of returning a struct:

================================================================== -- z1.c --------------------------------------------------------

struct s {
int x;
};

struct s func (int y)
{
struct s xx;
xx.x = y * 10;
return xx;
}

-- z2.c --------------------------------------------------------

struct s {
int x;
};

extern struct s func (int y);

void func2 (struct s x1, struct s x2)
{
printf ("%d %d\n", x1.x, x2.x);
}


int main ()
{
func2 (func (1), func (2));
}
----------------------------------------------------------------


On linux, compile z1.c with gcc 2.7.2 and z2.c with egcs and link them together.  The resulting program prints

10 1

rather than the expected

10 20

==========================================================




December 01, 2003
"Makaan Kublai Kahn" <Makaan_member@pathlink.com> wrote

>
> You can already do this.  You can return a struct I in C and C++.
> A struct can contain more than one value and encapsulate more than one
return
> of types.  It is better to return a struct of values than have a multiple
return
> for functions.  This is how it was intended to return multiple types.
>

Yes, but why should you have to define a structure just because you have to return two or three values rather than one?  It is more code to write.  The compiler knows all of the relevent information to define the structure implicitly.  In other words the compiler is responsible for generating the struct.  For example.

FOR THE CALLER SIDE
===================
D code as written:

    int x ;
    float y ;
     (x, y) = func(10, 3.14159, "foo") ;

The compiler then compiles it as if I had written:

    int x ;
    float y ;
    { /* compiler generated block statement */
        struct tempStruct {
            int var1 ;
            float var2 ;
        }
        tempStruct temp ;
        temp = func(10, 3.14159, "foo") ;
        x = temp.var1 ;
        y = temp.var2 ;
    }

FOR THE CALLEE SIDE
===================
D code as written:

     (int , float) = func(int a, float b, char [] c) {
        int result1 ;
        float result2 ;

        /* do stuff */

        return( result1, result2 ) ;
    }

The compiler then compiles it as if I had written:

     (int , float) = func(int a, float b, char [] c) {
        int result1 ;
        float result2 ;

        /* do stuff */

         { /* compiler generated block statement */
            struct tempStruct {
                int var1 ;
                float var2 ;
            }
            tempStruct temp ;
            temp.var1 = result1 ;
            temp.var2 = result2 ;
            return temp ;
        }/* end compiler generated block statement */
    }

This is also what happens to the aruments as well.  They are stuffed into an implicitly defined structure consisting of either the registers and/or the stack in a well defined way.  It is only natural to do the same thing in reverse.  (i.e. use the registers and stack to pass back multiple return values in a well defined way.  In this case our ficticious compiler used a struct, in an actual implementation it would use some registers and then the stack.)

mark.

> A link for gcc is:  http://gcc.gnu.org/ml/gcc/1998-11/msg01087.html
>
> Here's an example from that link of returning a struct:
>
> ================================================================== -- z1.c --------------------------------------------------------
>
> struct s {
> int x;
> };
>
> struct s func (int y)
> {
> struct s xx;
> xx.x = y * 10;
> return xx;
> }
>
> -- z2.c --------------------------------------------------------
>
> struct s {
> int x;
> };
>
> extern struct s func (int y);
>
> void func2 (struct s x1, struct s x2)
> {
> printf ("%d %d\n", x1.x, x2.x);
> }
>
>
> int main ()
> {
> func2 (func (1), func (2));
> }
> ----------------------------------------------------------------
>
>
> On linux, compile z1.c with gcc 2.7.2 and z2.c with egcs and link them together.  The resulting program prints
>
> 10 1
>
> rather than the expected
>
> 10 20
>
> ==========================================================
>
>
>
>


December 01, 2003
In article <bqgbtf$2726$1@digitaldaemon.com>, Mark J. Brudnak says...
>"Makaan Kublai Kahn" <Makaan_member@pathlink.com> wrote
>> You can already do this.  You can return a struct I in C and C++. A struct can contain more than one value and encapsulate more than one return of types.  It is better to return a struct of values than have a multiple return for functions.  This is how it was intended to return multiple types.

Functions returning several values could be used like

(a,b,c) = func(d,e,f,g);

Using this we could make the code clearer and easier
to understand if we decided that (d,e,f,g) could only
be in-parameters, and that the only way to get out-
functionality is via return values (e.g. (a,b,c)).

This would make the code quite readable. But the cost,
and the pre-requisite, would be to skip out-parameters
entirely from the language.

Why? Well, if we are browsing code written by others,
then we either can rely on the right side being input-
only, or then we can't. Only if we can do that, the
code becomes readily browsable for us.

This brings up the question, should we do this?

I for one would think that this is a truly interesting
aspect, and it may contain quite some promises. OTOH,
this would be such a profound change in the language
that we may risk alienating C(++), Java, and other
users. So, this had better be worth the cost.

Also, if we went for this kind of strategy, then why
not make both the in- (the right side) and the out-
(the left side) streamable! That is, a function
could take any number of parameters, and it could
also return an arbitrary number of parameters!
Well, such languages exist already. APL I think
is one. But from what I've had to program with
APL, this sure doesn't contribute to the overall
readability when it comes right down to it.

Then there is another solution. A company using D
could internally decide that every function takes
one in-parameter and one out-parameter. They would
be structs. This would of course lead to assignments
both at the input and the output, but since Good
Code has short functions, most of the locals could
be in structs to begin with. Standardising these
structs could result in increased clarity and maybe
even more overall structure in the program.

And we could skip a profound remake of D.



December 02, 2003
> Also, if we went for this kind of strategy, then why
> not make both the in- (the right side) and the out-
> (the left side) streamable! That is, a function
> could take any number of parameters, and it could
> also return an arbitrary number of parameters!
> Well, such languages exist already. APL I think
> is one. But from what I've had to program with
> APL, this sure doesn't contribute to the overall
> readability when it comes right down to it.

MATLAB allows both a variable number of inputs and outputs. It does this by
reserving the parameter names "varargin" and "varargout" to have special
meaning (they collect all the remaining inputs or outputs into a list). It
also defines the variables "nargin" and "nargout" in the function body to be
the number of inputs and outputs.
Multiple outputs are used very often in MATLAB code. varargout is used much
less frequently, and most uses probably are to implement generic function
evaluators.

-Ben


December 02, 2003
"Ben Hinkle" <bhinkle4@juno.com> wrote

>
> MATLAB allows both a variable number of inputs and outputs. It does this
by
> reserving the parameter names "varargin" and "varargout" to have special meaning (they collect all the remaining inputs or outputs into a list). It also defines the variables "nargin" and "nargout" in the function body to
be
> the number of inputs and outputs.
> Multiple outputs are used very often in MATLAB code. varargout is used
much
> less frequently, and most uses probably are to implement generic function evaluators.
>
> -Ben
>
>

Yes, but

Matlab != D

Matlab is interpreted and is therefore slow.  Don't get me wrong, I love Matlab, but when speed is necessary, a compiled, optimized language is necesary.  For me, I like C, I avoid C++ and D is appealing.  Since D is still being developed, multiple returns seem like a nice addition.

Mark.


December 02, 2003
How would multiple returns work if you don't provide places for the returns to go?
ie
(int, int) = someFunc(int);

(a) = someFunc(10);

Further to that, how would nested functions work?
(int) = someFunc(int, int, int)
(int, int, int) = foo(float)

(a) = someFunc(foo(10.0));

What about determining which function to call based on the return type?
(int) = foo(int)
(int, int) = foo(int)

(a,b) = foo(10);
c = foo(5);

I don't know how complex that is starting to get for the compiler.  I like the idea of mulitple returns, it can be very handy - but I get the feeling that it is probably quite hard to implement in a compiler.

Brad

> "Ben Hinkle" <bhinkle4@juno.com> wrote
> 
> 
>>MATLAB allows both a variable number of inputs and outputs. It does this
> 
> by
> 
>>reserving the parameter names "varargin" and "varargout" to have special
>>meaning (they collect all the remaining inputs or outputs into a list). It
>>also defines the variables "nargin" and "nargout" in the function body to
> 
> be
> 
>>the number of inputs and outputs.
>>Multiple outputs are used very often in MATLAB code. varargout is used
> 
> much
> 
>>less frequently, and most uses probably are to implement generic function
>>evaluators.
>>
>>-Ben
>>
>>
> 
> 
> Yes, but
> 
> Matlab != D
> 
> Matlab is interpreted and is therefore slow.  Don't get me wrong, I love
> Matlab, but when speed is necessary, a compiled, optimized language is
> necesary.  For me, I like C, I avoid C++ and D is appealing.  Since D is
> still being developed, multiple returns seem like a nice addition.
> 
> Mark.
> 
>