December 13, 2006
"Walter Bright" <newshound@digitalmars.com> wrote in message news:elnv14$r2d$1@digitaldaemon.com...

> For whatever the merits of opCall vs this(), efficiency is NOT a problem with either, and is not a reason to choose one or the other. The generated code is the same.
>
> Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.

Alright then.  All I've got then is the orthogonality argument.  But you won't listen to that either.

People will come to D from C++ and C# and ask "where are constructors in structs?" and we'll say "you have to use static opCall."  And they'll ask "why?"  And all we'll be able to do is shake our heads, sigh, and say "I don't know."

For the last time, even though you don't care: we have been using static opCall as a WORKAROUND.  As in *we never intended for that to be the canonical method of constructing a struct*.  I don't think anyone would complain if they were given a consistent, logical method of initializing any aggregate type.


December 13, 2006
On Wed, 13 Dec 2006 12:02:37 -0500, Jarrett Billingsley <kb3ctd2@yahoo.com> wrote:

> "Walter Bright" <newshound@digitalmars.com> wrote in message
> news:elnv14$r2d$1@digitaldaemon.com...
>
>> For whatever the merits of opCall vs this(), efficiency is NOT a problem
>> with either, and is not a reason to choose one or the other. The generated
>> code is the same.
>>
>> Under the hood, they are the same. Both take a hidden pointer to where the
>> result is stored. The rest is window dressing.
>
> Alright then.  All I've got then is the orthogonality argument.  But you
> won't listen to that either.
>
> People will come to D from C++ and C# and ask "where are constructors in
> structs?" and we'll say "you have to use static opCall."  And they'll ask
> "why?"  And all we'll be able to do is shake our heads, sigh, and say "I
> don't know."
>
> For the last time, even though you don't care: we have been using static
> opCall as a WORKAROUND.  As in *we never intended for that to be the
> canonical method of constructing a struct*.  I don't think anyone would
> complain if they were given a consistent, logical method of initializing any
> aggregate type.
>

For the record, I'm fine with static opCall. Structs don't have constructors and destructors, but the language just so happens to allow us to have something that looks like a constructor. We could use Foo.create() to initialize new Foo`s but since opCall is available and looks nice to us, we can go with it. If opCall didn't exist, would we even be having this argument? We would know structs weren't meant to have true constructors and destructors. We have an aesthetically pleasing way to initialize structs, so what's the problem. If your friend asks if structs have constructors, you say no, but since the langauge is so expressive, you have this feature called opCall that makes it just as nice to initialize.

P.S.  I actually don't want a defensive response to this, I'm just stating my view; thanks.
December 13, 2006
On Wed, 13 Dec 2006 12:21:37 -0500, Chris Miller wrote:

"We know structs weren't meant to have true constructors and destructors. We have an aesthetically pleasing way to initialize structs, so what's the problem. If your friend asks if structs have constructors, you say no, but since the langauge is so expressive, you have this feature called opCall that makes it just as nice to initialize."

*Amen*
December 13, 2006
Mark Wrenn wrote:
> On Wed, 13 Dec 2006 12:21:37 -0500, Chris Miller wrote:
> 
> "We know structs weren't meant to have true constructors and destructors.
> We have an aesthetically pleasing way to initialize structs, so what's the
> problem. If your friend asks if structs have constructors, you say no, but
> since the langauge is so expressive, you have this feature called opCall
> that makes it just as nice to initialize."
> 
> *Amen*

If structs do add destructors, this can will be reopened - and by that time it will be much more smellier.

Andrei
December 13, 2006
Stewart Gordon wrote:
> Walter Bright wrote:
>> Stewart Gordon wrote:
>>> Walter Bright wrote:
>>>> I don't think that'll work. He'll wind up being forced to set all the fields.
>>> Doesn't follow - there might not be any to set other than those that are
>>> an inherent part of the assignment operation.
>>
>> But if there *are* other fields that shouldn't be set, a rule to preclude those cases would be a problem.
> 
> Exactly.  *If* there are other fields that shouldn't be set.  Do you even understand a word of what Chris is proposing?
> 
> The programmer would have a choice - opAssign returning void to modify in-place the object referenced by the lvalue, or returning a new object that will be assigned to the lvalue.  What is this precluding?

Just a few thoughts.  If this change is implemented, it should apply to all assign ops, not just opAssign itself.  So opAddAssign, opMulAssign, etc.  Also, it seems like this could give rise to some interesting behavior:

    class MyClass
    {
        int val;

        this( int x )
        {
            val = x;
        }

        MyClass opAssign( int x )
        {
            return new MyClass( x );
        }
    }

    void fn( MyClass c )
    {
        c = 42;
    }

    MyClass c = new MyClass( 0 );
    fn( c );
    writefln( c.val );

Based on this suggestion, the above will print '0', not '42'.  This is actually kind of an interesting situation, as it offers the possibility of making non-inout reference parameters immutable with respect to assign operations.  The other consequence being that assign operations could change the address of a class reference as a side-effect, which may have an impact on optimization.  It also may interact somewhat oddly with associative arrays and such, depending on how the compiler generates code.  ie.

    MyClass[int] aa;
    aa[0] = new MyClass( 0 );
    aa[0] = 42;
    writefln( aa[0].val );

What will the above print?  This sort of behavior would need to be defined in the spec.


Sean
December 14, 2006
Stewart Gordon wrote:
> Walter Bright wrote:
>> Stewart Gordon wrote:
>>> Walter Bright wrote:
>>>> I don't think that'll work. He'll wind up being forced to set all the fields.
>>> Doesn't follow - there might not be any to set other than those that are
>>> an inherent part of the assignment operation.
>>
>> But if there *are* other fields that shouldn't be set, a rule to preclude those cases would be a problem.
> 
> Exactly.  *If* there are other fields that shouldn't be set.  Do you even understand a word of what Chris is proposing?
> 
> The programmer would have a choice - opAssign returning void to modify in-place the object referenced by the lvalue, or returning a new object that will be assigned to the lvalue.  What is this precluding?

opAssign has 3 externally visible characteristics:

1) the parameter
2) the 'this' pointer
3) the return value

Your proposal mixes up 2 and 3. opAssign works like:

	a = b
becomes:
	a.opAssign(b)

The return value is not assigned to a, it is the value of the expression (a = b). Mixing up the return value and the assignment to a will cause problems, as the two are different things, and should be independent.
December 14, 2006
Don Clugston wrote:
> Intriguing. Evidently the ideas barrel is far from empty. <g>

No chance of that happening <g>.
December 14, 2006
BCS wrote:
> With constructors, it is not only simpler code, but looks like what is happening.
> 
> struct S
> {
>     static S err;
>     int k, l;
> 
>     this(int i, int j)
>     {
>         k=i;
>         l=j;
> 
>         if(!ret.test) this = err;
>     }
> 
>     bool test(){...}
> }

Assignment to this inside a constructor is a mistake as it breaks the assumptions the language makes about constructors.
December 14, 2006
Andrei Alexandrescu (See Website For Email) wrote:
> Walter Bright wrote:
>> Under the hood, they are the same. Both take a hidden pointer to where the result is stored. The rest is window dressing.
> 
> That means you can't return the result in a register :o).

Actually, it does return it in a register if it fits in one. Try it!
December 14, 2006
Walter Bright wrote:
> opAssign has 3 externally visible characteristics:
> 
> 1) the parameter
> 2) the 'this' pointer
> 3) the return value
> 
> Your proposal mixes up 2 and 3. opAssign works like:
> 
>     a = b
> becomes:
>     a.opAssign(b)
> 
> The return value is not assigned to a, it is the value of the expression (a = b). Mixing up the return value and the assignment to a will cause problems, as the two are different things, and should be independent.

That makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost?

What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?


Andrei