www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Public outcry against new .init behaviour

reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
With pre-1.017/2.001 .init:

typedef int foo = 1;
foo x = 3; // x.init is 3
foo y;     // y.init is 1

With new .init:

foo x = 3; // x.init is 1
foo y;     // y.init is 1

My question is, why? The only argument I've heard is consistency, such that
foo.property == typeof(foo).property, as in:

struct S { static int x; }
S s; // s.x == S.x, &s.x == &S.x

Yet there's no such consistency in place regardless of .init, and I really don't
understand this argument. What if x isn't static? S.x becomes an error.
Consistency lost!

The only way I can understand the above is that it's considered problematic that
.init is one of two properties which are valid for both a variable and its type,
and may give different results for each. If this is really that bad, consider
creating a new property, and make .stringof behave the same way. (Also, the
documentation at http://www.digitalmars.com/d/property.html still refers to the
old .init behaviour.)

There are significant arguments for the old behaviour of .init. The primary use,
in my experience, is to initialize variables to invalid values, and then one can
simply compare to .init to see if the variable has become valid. It's similar to
how static arrays simplify the C idiom:

#define FOO_LENGTH 3
int[FOO_LENGTH] foo;

In D, we can just write int[3] foo and use foo.length. However, with the new
.init, we're forced to use similar code:

typedef int foo = 1;
invariant foo X_INIT = 3; // "const" instead of "invariant" for D 1.0
foo x = X_INIT;

As opposed to what could be just foo x = 3 and x.init.

It's not deadly - I found 14 instances of such in about 7000 LOC (wc -l), and
they can be corrected - but it makes code noticeably uglier. This is one of
those small, simple bits of syntactic sugar which make coding in D fun and
productive. I want it back.

-- 
Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
next sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Report it as a bug/regression? It certainly shouldn't have been changed in
the 1.x branch (since stability is the point of a branch in the first
place), and neither in the 2.x branch without providing an easy upgrade
path for those needing the old behaviour.

Deewiant wrote:

 With pre-1.017/2.001 .init:
 
 typedef int foo = 1;
 foo x = 3; // x.init is 3
 foo y;     // y.init is 1
 
 With new .init:
 
 foo x = 3; // x.init is 1
 foo y;     // y.init is 1
 
 My question is, why? The only argument I've heard is consistency, such
 that foo.property == typeof(foo).property, as in:
 
 struct S { static int x; }
 S s; // s.x == S.x, &s.x == &S.x
 
 Yet there's no such consistency in place regardless of .init, and I really
 don't understand this argument. What if x isn't static? S.x becomes an
 error. Consistency lost!
 
 The only way I can understand the above is that it's considered
 problematic that .init is one of two properties which are valid for both a
 variable and its type, and may give different results for each. If this is
 really that bad, consider creating a new property, and make .stringof
 behave the same way. (Also, the documentation at
 http://www.digitalmars.com/d/property.html still refers to the old .init
 behaviour.)
 
 There are significant arguments for the old behaviour of .init. The
 primary use, in my experience, is to initialize variables to invalid
 values, and then one can simply compare to .init to see if the variable
 has become valid. It's similar to how static arrays simplify the C idiom:
 
 #define FOO_LENGTH 3
 int[FOO_LENGTH] foo;
 
 In D, we can just write int[3] foo and use foo.length. However, with the
 new .init, we're forced to use similar code:
 
 typedef int foo = 1;
 invariant foo X_INIT = 3; // "const" instead of "invariant" for D 1.0
 foo x = X_INIT;
 
 As opposed to what could be just foo x = 3 and x.init.
 
 It's not deadly - I found 14 instances of such in about 7000 LOC (wc -l),
 and they can be corrected - but it makes code noticeably uglier. This is
 one of those small, simple bits of syntactic sugar which make coding in D
 fun and productive. I want it back.
 

-- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Jul 02 2007
parent Peter Modzelewski <peter.modzelewski gmail.com> writes:
Lars Ivar Igesund napisa?(a):
 Report it as a bug/regression? It certainly shouldn't have been changed in
 the 1.x branch (since stability is the point of a branch in the first
 place), and neither in the 2.x branch without providing an easy upgrade
 path for those needing the old behaviour.
 

My feelings the same. 1.x shouldn't be touched except for optimizations and bugfixes. Any idea can be introduced in 2.x but also with solid discussion especially from the experienced programmers' side. I consider changing .init in 1.x branch as a mistake which should be fixed as quick as possible.
Jul 02 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Deewiant wrote:
 It's not deadly - I found 14 instances of such in about 7000 LOC (wc -l), and
 they can be corrected - but it makes code noticeably uglier. This is one of
 those small, simple bits of syntactic sugar which make coding in D fun and
 productive. I want it back.

I'll repost what I did earlier on this:
 Andrei made an argument that if one had:
 
     struct S
     {
         static int foo;
     }
 
     S s = ...;
     assert(s.foo == S.foo);
 
 then, analogously:
 
     T t = ...;
     assert(t.init == T.init);
 
 should hold as well. Consistency is a strong argument.

I like the old behavior too, but with the increasing use of generic code, I worry that the inconsistency is going to cause a lot of problems in the future.
Jul 02 2007
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
I agree.... But the change should only be introduced to the 2.x branch...
Breaking changes have no place in the stable branch.

Maybe you could add a different property that can get the old behavior for
variables, too...?

Walter Bright Wrote:

 Deewiant wrote:
 It's not deadly - I found 14 instances of such in about 7000 LOC (wc -l), and
 they can be corrected - but it makes code noticeably uglier. This is one of
 those small, simple bits of syntactic sugar which make coding in D fun and
 productive. I want it back.

I'll repost what I did earlier on this:
 Andrei made an argument that if one had:
 
     struct S
     {
         static int foo;
     }
 
     S s = ...;
     assert(s.foo == S.foo);
 
 then, analogously:
 
     T t = ...;
     assert(t.init == T.init);
 
 should hold as well. Consistency is a strong argument.

I like the old behavior too, but with the increasing use of generic code, I worry that the inconsistency is going to cause a lot of problems in the future.

Jul 02 2007
parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Mon, 02 Jul 2007 21:07:48 +0300, Robert Fraser <fraserofthenight gmai=
l.com> wrote:

 I agree.... But the change should only be introduced to the 2.x branch=

 Maybe you could add a different property that can get the old behavior=

I concur - how about a new .default property? It's pretty clear that it = resolves to the default value for this type (x.default=3D=3DX.default), = unlike .init, which is the value this variable is initialised to (and is= the same as .default for types) :) Also, agreed that the old behavior ought to be reverted for 1.x and this= would only go to 2.x. -- = Best regards, Vladimir mailto:thecybershadow gmail.com
Jul 02 2007
parent Xinok <xnknet gmail.com> writes:
Vladimir Panteleev wrote:
 On Mon, 02 Jul 2007 21:07:48 +0300, Robert Fraser <fraserofthenight gmail.com>
wrote:
 
 I agree.... But the change should only be introduced to the 2.x branch...
Breaking changes have no place in the stable branch.

 Maybe you could add a different property that can get the old behavior for
variables, too...?

I concur - how about a new .default property? It's pretty clear that it resolves to the default value for this type (x.default==X.default), unlike .init, which is the value this variable is initialised to (and is the same as .default for types) :) Also, agreed that the old behavior ought to be reverted for 1.x and this would only go to 2.x.

I think that's the best solution, revert .init to it's old behavior, and add a new property to reflect it's new behavior. Of course, .default probably couldn't be used since that's a keyword. I'm only using "typeinit" as an example, I'm sure there's a better word that could be used. typeinit would simply be an alias for typeof(t).init typedef int T = 3; T v1 = 5; // v1.init == 5, v1.typeinit == 3 T v2; // v1.init == 3, v1.typeinit == 3
Jul 02 2007
prev sibling next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Walter Bright wrote:
 I'll repost what I did earlier on this:
 
 Andrei made an argument that if one had:

     struct S
     {
         static int foo;
     }

     S s = ...;
     assert(s.foo == S.foo);

 then, analogously:

     T t = ...;
     assert(t.init == T.init);

 should hold as well. Consistency is a strong argument.


Yes, I mentioned that in my post.
 I like the old behavior too, but with the increasing use of generic
 code, I worry that the inconsistency is going to cause a lot of problems
 in the future.

I don't see why such problems can't be avoided by just always using typeof(variable).init when that's what is really meant. Premature fixing of problems is a bad idea. Regarding 1.0 and 2.0, this definitely shouldn't be incorporated into 1.0, but I'm also arguing against adding it to 2.0. We don't need an additional property since you can just keep the current behaviour and use x.init and typeof(x).init. Adding a property would just make it so that it's x.someproperty and (x.init or typeof(x).init). Needless duplication. -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
parent reply kris <foo bar.com> writes:
(removed earlier post on this topic)


Seems like there's more subtlety to this than Walter has explained? From 
the description of the change, I'd asserted that the following code 
would be impacted:

# typedef int socket_t = -1;
# ...
# socket_t sock;
# ...
# if (sock is sock.init)
# ...

However, upon inspection, the codegen for v1.017 is actually this:

# if (sock is sock.init)
# 004044E6 83 7D 08 FF cmp dword ptr [sock],0FFh

meaning that the typedef value has been respected, just like v0.016

Either this means that Walter missed typedef in his changes, or that 
typedef is intended to remain as it was.

Which is it?





Deewiant wrote:
 Walter Bright wrote:
 I'll repost what I did earlier on this:

 Andrei made an argument that if one had:

     struct S
     {
         static int foo;
     }

     S s = ...;
     assert(s.foo == S.foo);

 then, analogously:

     T t = ...;
     assert(t.init == T.init);

 should hold as well. Consistency is a strong argument.


Yes, I mentioned that in my post.
 I like the old behavior too, but with the increasing use of generic
 code, I worry that the inconsistency is going to cause a lot of problems
 in the future.

I don't see why such problems can't be avoided by just always using typeof(variable).init when that's what is really meant. Premature fixing of problems is a bad idea. Regarding 1.0 and 2.0, this definitely shouldn't be incorporated into 1.0, but I'm also arguing against adding it to 2.0. We don't need an additional property since you can just keep the current behaviour and use x.init and typeof(x).init. Adding a property would just make it so that it's x.someproperty and (x.init or typeof(x).init). Needless duplication.

Jul 02 2007
parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
kris wrote:
 Seems like there's more subtlety to this than Walter has explained? From
 the description of the change, I'd asserted that the following code
 would be impacted:
 
 # typedef int socket_t = -1;
 # ...
 # socket_t sock;
 # ...
 # if (sock is sock.init)
 # ...
 
 However, upon inspection, the codegen for v1.017 is actually this:
 
 # if (sock is sock.init)
 # 004044E6 83 7D 08 FF cmp dword ptr [sock],0FFh
 
 meaning that the typedef value has been respected, just like v0.016
 
 Either this means that Walter missed typedef in his changes, or that
 typedef is intended to remain as it was.
 
 Which is it?
 

If you change that code to "socket_t sock = 0;" then you'll see the difference. Pre-1.017 would have compared to 0, now it always compares to -1. -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
prev sibling parent reply "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Mon, 02 Jul 2007 21:04:09 +0300, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Deewiant wrote:
 It's not deadly - I found 14 instances of such in about 7000 LOC (wc =


 -l), and
 they can be corrected - but it makes code noticeably uglier. This is =


 one of
 those small, simple bits of syntactic sugar which make coding in D fu=


 and
 productive. I want it back.

I'll repost what I did earlier on this:
 Andrei made an argument that if one had:
      struct S
     {
         static int foo;
     }
      S s =3D ...;
     assert(s.foo =3D=3D S.foo);
  then, analogously:
      T t =3D ...;
     assert(t.init =3D=3D T.init);
  should hold as well. Consistency is a strong argument.

I like the old behavior too, but with the increasing use of generic =

 code, I worry that the inconsistency is going to cause a lot of proble=

 in the future.

I fail to see how this is a matter of consistency. To me, it's a matter = of = definition: 't.init' means the initialization value of the *variable* 't'. 'T.init' means the initialization value of the *type* 'T'. Now 't.init' is meaningless. Well, if one wants consistency ;) , lets consider the following: struct Z { static int x; int y; } Z a, b; Because 'a.x =3D=3D b.x' holds, 'a.y =3D=3D b.y' should hold too? <g>
Jul 02 2007
next sibling parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Tue, 03 Jul 2007 00:20:39 +0300, Kristian Kilpi <kjkilpi gmail.com>  =

wrote:
 On Mon, 02 Jul 2007 21:04:09 +0300, Walter Bright  =

 <newshound1 digitalmars.com> wrote:

 Deewiant wrote:
 It's not deadly - I found 14 instances of such in about 7000 LOC (wc=



 -l), and
 they can be corrected - but it makes code noticeably uglier. This is=



 one of
 those small, simple bits of syntactic sugar which make coding in D f=



 and
 productive. I want it back.

I'll repost what I did earlier on this:
 Andrei made an argument that if one had:
      struct S
     {
         static int foo;
     }
      S s =3D ...;
     assert(s.foo =3D=3D S.foo);
  then, analogously:
      T t =3D ...;
     assert(t.init =3D=3D T.init);
  should hold as well. Consistency is a strong argument.

I like the old behavior too, but with the increasing use of generic =


 code, I worry that the inconsistency is going to cause a lot of  =


 problems in the future.

I fail to see how this is a matter of consistency. To me, it's a matte=

 of definition:
 't.init' means the initialization value of the *variable* 't'.
 'T.init' means the initialization value of the *type* 'T'.

 Now 't.init' is meaningless.


 Well, if one wants consistency ;) , lets consider the following:

    struct Z {
      static int x;
      int y;
    }
    Z a, b;

 Because 'a.x =3D=3D b.x' holds, 'a.y =3D=3D b.y' should hold too? <g>

Here is another (counter) example: ;) 't.init' did mean non-static init value (in old versions). 'T.init' means static init value. Now 't.init =3D=3D T.init' should hold. So, 'a.y =3D=3D a.x' should hold too? 'a.y' <-> 't.init' (non-static) 'a.x' <-> 'T.init' (static)
Jul 02 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Kristian Kilpi wrote:
 On Mon, 02 Jul 2007 21:04:09 +0300, Walter Bright 
 <newshound1 digitalmars.com> wrote:
 I like the old behavior too, but with the increasing use of generic 
 code, I worry that the inconsistency is going to cause a lot of 
 problems in the future.

I fail to see how this is a matter of consistency. To me, it's a matter of definition: 't.init' means the initialization value of the *variable* 't'. 'T.init' means the initialization value of the *type* 'T'.

For what it's worth, I actually had no idea that t.init was different from T.init. I must have simply overlooked it in the spec. But while it is an appealing idea, it presents tradeoffs that are perhaps not ideal. If the actual initialization value must be retained, then space must be reserved for it in functions where "t.init" is used. And it seems like this behavior could possibly change based on scope? ie. void fnA() { int x = 5; assert( fnB( x ) == x.init ); } void fnB( inout int x ) { return x.init; } Will the above pass or fail? Alternately, if space is not reserved for the init value, then the initializer must be run every time "t.init" is called. But what if this initializer has side effects? This is clearly not an acceptable solution.
 Now 't.init' is meaningless.

Not at all. It's simply a shorthand for "typeof(t).init".
 Well, if one wants consistency ;) , lets consider the following:
 
   struct Z {
     static int x;
     int y;
   }
   Z a, b;
 
 Because 'a.x == b.x' holds, 'a.y == b.y' should hold too? <g>

This is not a valid analogy. Sean
Jul 02 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Sean Kelly wrote:
 For what it's worth, I actually had no idea that t.init was different
 from T.init.  I must have simply overlooked it in the spec.  But while
 it is an appealing idea, it presents tradeoffs that are perhaps not
 ideal.  If the actual initialization value must be retained, then space
 must be reserved for it in functions where "t.init" is used.  And it
 seems like this behavior could possibly change based on scope?  ie.
 
     void fnA() {
         int x = 5;
         assert( fnB( x ) == x.init );
     }
 
     void fnB( inout int x ) {
         return x.init;
     }
 
 Will the above pass or fail?
 
 Alternately, if space is not reserved for the init value, then the
 initializer must be run every time "t.init" is called.  But what if this
 initializer has side effects?  This is clearly not an acceptable solution.
 

The code will fail, because x.init here is the .init of x in that scope. Here, it's always typeof(x).init, but if the x in the parameter list were to have a default initializer (doesn't work in the case of inout), x.init would be that: void fnB(int x = 3) { /+ x.init is 3 +/ } A good rule is that if a variable is initialized to a compile-time constant, variable.init is that constant. Otherwise, variable.init is typeof(variable).init. (My terminology might be a bit wrong: it's not the variable, it's the symbol of a variable in a scope? Whatever works so that it explains function parameters, too.) I wouldn't mind if variable.init were an error in the latter cases, but the above rule can be used to always figure out what it means. (To be pedantic, your code will fail also because fnB returns void.) -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
parent reply Sean Kelly <sean f4.ca> writes:
Deewiant wrote:
 Sean Kelly wrote:
 For what it's worth, I actually had no idea that t.init was different
 from T.init.  I must have simply overlooked it in the spec.  But while
 it is an appealing idea, it presents tradeoffs that are perhaps not
 ideal.  If the actual initialization value must be retained, then space
 must be reserved for it in functions where "t.init" is used.  And it
 seems like this behavior could possibly change based on scope?  ie.

     void fnA() {
         int x = 5;
         assert( fnB( x ) == x.init );
     }

     void fnB( inout int x ) {
         return x.init;
     }

 Will the above pass or fail?

 Alternately, if space is not reserved for the init value, then the
 initializer must be run every time "t.init" is called.  But what if this
 initializer has side effects?  This is clearly not an acceptable solution.

The code will fail, because x.init here is the .init of x in that scope. Here, it's always typeof(x).init, but if the x in the parameter list were to have a default initializer (doesn't work in the case of inout), x.init would be that: void fnB(int x = 3) { /+ x.init is 3 +/ }

Yeah, now that I'm aware of the old rule I think it makes total sense, and I'm not entirely sure why it has to be done away with--value.init was just an alias for the local initializer. Since ctors (static or otherwise) are effectively not at class or global scope, there's little chance of losing an initialization in a huge pile of code.
 A good rule is that if a variable is initialized to a compile-time constant,
 variable.init is that constant. Otherwise, variable.init is
 typeof(variable).init. (My terminology might be a bit wrong: it's not the
 variable, it's the symbol of a variable in a scope? Whatever works so that it
 explains function parameters, too.)

I'm really fine with it either way. I think the old approach was a neat trick and in hindsight made sense to me, but the new approach is consistent and straightforward. I generally use .init in a context where the change doesn't matter, so...
 I wouldn't mind if variable.init were an error in the latter cases, but the
 above rule can be used to always figure out what it means.
 
 (To be pedantic, your code will fail also because fnB returns void.)

Oops. Sean
Jul 02 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Sean Kelly wrote:
 I generally use .init in a context where the change doesn't matter, so...

I'd find such code easier to read if you used typeof(foo).init, it's clearer if that's what you really mean. I've come across this in Tango once or twice: I was really bewildered to find out that "foo.nan" is equivalent to typeof(foo).nan, I was wondering if it had something to do with NaN payloads. -- Remove ".doesnotlike.spam" from the mail address.
Jul 02 2007
next sibling parent reply kris <foo bar.com> writes:
Deewiant wrote:
 Sean Kelly wrote:
 I generally use .init in a context where the change doesn't matter, so...

I'd find such code easier to read if you used typeof(foo).init, it's clearer if that's what you really mean. I've come across this in Tango once or twice: I was really bewildered to find out that "foo.nan" is equivalent to typeof(foo).nan, I was wondering if it had something to do with NaN payloads.

Shortcuts aside, I agree with Walter on this one (yeah, I know - hell just froze over). Sorry, Deewiant :( It becomes clear once you start messing with declarations: # struct S {int x=7; bool y = true;} # typedef int Sock = -1; In both cases, the assignment of a value is associated with a /type declaration/. This creates a .init value in an appropriate manner for S and for Sock. On the other hand, the simple act of assigning a value to a /type instance/ should not be associated with .init values i.e. # int x = 42; Should not affect the .init of x in any manner, since it is not a type declaration. By doing simple assignment only, the compiler helps the programmer to avoid some latent bugs. For example, suppose I'm maintaining some code that looks like this: # int x; # x = 42; and decide that those ought to be folded together while I'm cleaning up something else? Ouch! Potentially nasty bug In other words, assigning .init values should be associated with type-declaration only, and not with type-instancing. This is a simple rule to comprehend - one that gravitates toward the principal of least surprise?
Jul 02 2007
next sibling parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
kris wrote:

 For example, suppose I'm
 maintaining some code that looks like this:
 
 # int x;
 # x = 42;
 
 and decide that those ought to be folded together while I'm cleaning up
 something else? Ouch! Potentially nasty bug
 
 In other words, assigning .init values should be associated with
 type-declaration only, and not with type-instancing. This is a simple
 rule to comprehend - one that gravitates toward the principal of least
 surprise?

I agree with this too. Those initial values could be placed to a constant. Language consistency is more important than avoiding the need for very little extra typing.
Jul 03 2007
parent Paul <paul.justtheletterd.anderson comcast.net> writes:
Jari-Matti Mäkelä Wrote:

 kris wrote:
 
 For example, suppose I'm
 maintaining some code that looks like this:
 
 # int x;
 # x = 42;
 
 and decide that those ought to be folded together while I'm cleaning up
 something else? Ouch! Potentially nasty bug
 
 In other words, assigning .init values should be associated with
 type-declaration only, and not with type-instancing. This is a simple
 rule to comprehend - one that gravitates toward the principal of least
 surprise?

I agree with this too. Those initial values could be placed to a constant. Language consistency is more important than avoiding the need for very little extra typing.

I agree. The .init value should be tied to the type and not the variable. Initializing the variable to a const/invariant/whatever value and checking against that value is not onerous, IMHO. Paul
Jul 03 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
kris wrote:
 Deewiant wrote:
 Sean Kelly wrote:
 I generally use .init in a context where the change doesn't matter, 
 so...

I'd find such code easier to read if you used typeof(foo).init, it's clearer if that's what you really mean. I've come across this in Tango once or twice: I was really bewildered to find out that "foo.nan" is equivalent to typeof(foo).nan, I was wondering if it had something to do with NaN payloads.

Shortcuts aside, I agree with Walter on this one (yeah, I know - hell just froze over). Sorry, Deewiant :( It becomes clear once you start messing with declarations: # struct S {int x=7; bool y = true;} # typedef int Sock = -1; In both cases, the assignment of a value is associated with a /type declaration/. This creates a .init value in an appropriate manner for S and for Sock. On the other hand, the simple act of assigning a value to a /type instance/ should not be associated with .init values i.e. # int x = 42; Should not affect the .init of x in any manner, since it is not a type declaration. By doing simple assignment only, the compiler helps the programmer to avoid some latent bugs. For example, suppose I'm maintaining some code that looks like this: # int x; # x = 42; and decide that those ought to be folded together while I'm cleaning up something else? Ouch! Potentially nasty bug In other words, assigning .init values should be associated with type-declaration only, and not with type-instancing. This is a simple rule to comprehend - one that gravitates toward the principal of least surprise?

I was mostly undecided on this issue, as Walter/Andrei's argument didn't convince me much (I didn't see that as a consistency breach), but kris's post on the other hand makes a very good case for current .init behavior. The only way I see to satisfy both sides would be to have two properties, for these two different concepts: the type's default initializer (current .init), and the variable declaration initializer (old .init). Additionally the second property should not exist on types, only on variables (ie, 'Foo.oldinit' is illegal), and maybe it should also be illegal for variables which do not have an initializer (ie, 'int foo; foo.oldinit;' is illegal). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 04 2007
parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Wed, 04 Jul 2007 13:45:28 +0300, Bruno Medeiros  =

<brunodomedeiros+spam com.gmail> wrote:
 kris wrote:
 Deewiant wrote:
 Sean Kelly wrote:
 I generally use .init in a context where the change doesn't matter,=




 so...

I'd find such code easier to read if you used typeof(foo).init, it's=



 clearer if
 that's what you really mean. I've come across this in Tango once or =



 twice: I was
 really bewildered to find out that "foo.nan" is equivalent to  =



 typeof(foo).nan, I
 was wondering if it had something to do with NaN payloads.



 just froze over). Sorry, Deewiant :(
  It becomes clear once you start messing with declarations:
  # struct S {int x=3D7; bool y =3D true;}
 # typedef int Sock =3D -1;
  In both cases, the assignment of a value is associated with a /type =


 declaration/. This creates a .init value in an appropriate manner for=


 and for Sock.
  On the other hand, the simple act of assigning a value to a /type  =


 instance/ should not be associated with .init values i.e.
  # int x =3D 42;
  Should not affect the .init of x in any manner, since it is not a ty=


 declaration. By doing simple assignment only, the compiler helps the =


 programmer to avoid some latent bugs. For example, suppose I'm  =


 maintaining some code that looks like this:
  # int x;
 # x =3D 42;
  and decide that those ought to be folded together while I'm cleaning=


 up something else? Ouch! Potentially nasty bug
  In other words, assigning .init values should be associated with  =


 type-declaration only, and not with type-instancing. This is a simple=


 rule to comprehend - one that gravitates toward the principal of leas=


 surprise?

I was mostly undecided on this issue, as Walter/Andrei's argument didn=

 convince me much (I didn't see that as a consistency breach), but kris=

 post on the other hand makes a very good case for current .init behavi=

 The only way I see to satisfy both sides would be to have two  =

 properties, for these two different concepts: the type's default  =

 initializer (current .init), and the variable declaration initializer =

 (old .init). Additionally the second property should not exist on type=

 only on variables (ie, 'Foo.oldinit' is illegal), and maybe it should =

 also be illegal for variables which do not have an initializer (ie, 'i=

 foo; foo.oldinit;' is illegal).

I agree. The current initializer (.init) is safer and simpler than the o= ld = one (.oldinit). However, having .oldinit too would be handy sometimes. And with Bruno's = = rules above, using .oldinit should be ok. Changing code from "int x =3D 42;" to "int x; x =3D 42", and vice versa,= can = still cause problems (code fails to compile). What if one must define .oldinit in a = = variable declaration in order to use it? int x.oldinit =3D 42; v =3D x.oldinit; //ok int y =3D 42; v =3D y.oldinit; //error This way the programmer will clearly see which value is 'assigned' to = .oldinit (and where). Or we could have the following syntax instead: int x =3D oldinit 42; (or maybe we should just stick with the current .init behaviour... :| )
Jul 04 2007
prev sibling parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
kris wrote:
 It becomes clear once you start messing with declarations:
 
 # struct S {int x=7; bool y = true;}
 # typedef int Sock = -1;
 
 In both cases, the assignment of a value is associated with a /type
 declaration/. This creates a .init value in an appropriate manner for S
 and for Sock.
 
 On the other hand, the simple act of assigning a value to a /type
 instance/ should not be associated with .init values i.e.
 
 # int x = 42;
 
 Should not affect the .init of x in any manner, since it is not a type
 declaration.

It shouldn't affect the .init of typeof(x). The .init of a type and a variable is different, it's only when no initializer is explicitly specified that the .init of the variable is equivalent to that of the type.
 By doing simple assignment only, the compiler helps the
 programmer to avoid some latent bugs. For example, suppose I'm
 maintaining some code that looks like this:
 
 # int x;
 # x = 42;
 
 and decide that those ought to be folded together while I'm cleaning up
 something else? Ouch! Potentially nasty bug

The bug is in your code, for using x.init instead of typeof(x).init when that's what you really meant.
 In other words, assigning .init values should be associated with
 type-declaration only, and not with type-instancing. This is a simple
 rule to comprehend - one that gravitates toward the principal of least
 surprise?

I think disallowing "x.init" when x has no explicit initializer would be the clearest. It invalidates bugs such as the above, because you're forced to use typeof(x).init. And we get to keep both behaviours for use as necessary, without some oldinit property. -- Remove ".doesnotlike.spam" from the mail address.
Jul 04 2007
prev sibling parent Sean Kelly <sean f4.ca> writes:
Deewiant wrote:
 Sean Kelly wrote:
 I generally use .init in a context where the change doesn't matter, so...

I'd find such code easier to read if you used typeof(foo).init, it's clearer if that's what you really mean. I've come across this in Tango once or twice: I was really bewildered to find out that "foo.nan" is equivalent to typeof(foo).nan, I was wondering if it had something to do with NaN payloads.

Yeah, it's worth considering. I tend to avoid comparing to T.init because then it's sensitive to type changes. typeof(t).init is just kind of verbose, but I agree that it's more clear. Sean
Jul 02 2007
prev sibling parent Jason House <jason.james.house gmail.com> writes:
Kristian Kilpi wrote:
 I fail to see how this is a matter of consistency. To me, it's a matter 
 of definition:
 't.init' means the initialization value of the *variable* 't'.
 'T.init' means the initialization value of the *type* 'T'.

I'd vote for this too. I liked the old t.init functionality. Having T.init *should* be enough. worst case scenario, someone must do typeof(t).init if they really want that and are deep in generic code.
Jul 02 2007
prev sibling parent BCS <ao pathlink.com> writes:
AAAAAAArrrrrrggggg

I unzipped 1.018 just a bit ago and Absolute first thing the new init nails 
me.
Jul 05 2007