Discussion:
[dart-misc] Why does 'Y ctor' print twice
Daniel Davidson
2016-06-16 17:26:36 UTC
Permalink
Is it an an inefficiency in the language, the implementation of the
language, or just a misinterpretation on my part?
I seems like an instance variable in a class should be initialized only
once - but this appears to be happening twice.

class Y {
Y() { print('Y ctor'); }
}

class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}

main() {
new X();
}


Thanks
Dan
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
'Bob Nystrom' via Dart Misc
2016-06-16 17:33:31 UTC
Permalink
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?

– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Daniel Davidson
2016-06-16 17:56:55 UTC
Permalink
I hoped that if the compiler sees the initialization in a in the ctor it
does not also execute the one in the member definition.
If that were the case a second ctor could ignore Y altogether and still get
a nice default.

class X {
X({ Y y }) : y = y ?? new Y();
X.fromNothing();
Y y = new Y();
}


So in the first ctor the user may pass in a y and if so it is used, if not
one is allocated, but just once.
But in the fromNothing ctor the compiler understands no initialization from
the ctor and uses the class version.

I guess my mental model is if a member has an initializer in the class and
the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Post by 'Bob Nystrom' via Dart Misc
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?
– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Daniel Davidson
2016-06-16 18:04:42 UTC
Permalink
For example, this C++ analogue does not initialize x twice and exhibits the
behavior I was expecting:


#include <iostream>

int getX()
{
std::cout << "Someone getting x" << std::endl;
return 42;
}

struct X {
X(int x) : x(x) {}
X() {}
int x = getX();
};

int main(int argc, char** argv) {

X x1(3);
std::cout << "x1 x->" << x1.x << std::endl;
X x2;
std::cout << "x2 x->" << x2.x << std::endl;

return 0;
}
Post by Daniel Davidson
I hoped that if the compiler sees the initialization in a in the ctor it
does not also execute the one in the member definition.
If that were the case a second ctor could ignore Y altogether and still
get a nice default.
class X {
X({ Y y }) : y = y ?? new Y();
X.fromNothing();
Y y = new Y();
}
So in the first ctor the user may pass in a y and if so it is used, if not
one is allocated, but just once.
But in the fromNothing ctor the compiler understands no initialization
from the ctor and uses the class version.
I guess my mental model is if a member has an initializer in the class and
the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Post by 'Bob Nystrom' via Dart Misc
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?
– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Danny Tuppeny
2016-06-16 18:15:15 UTC
Permalink
In this code:

y = y ?? new Y()

The first "y" is the field and its access causes the field initialiser to
run (which creates a Y).
The second "y" is the constructor arg, which is null, so the final part is
run (which creates a Y).

Isn't this expected?
Post by Daniel Davidson
For example, this C++ analogue does not initialize x twice and exhibits
#include <iostream>
int getX()
{
std::cout << "Someone getting x" << std::endl;
return 42;
}
struct X {
X(int x) : x(x) {}
X() {}
int x = getX();
};
int main(int argc, char** argv) {
X x1(3);
std::cout << "x1 x->" << x1.x << std::endl;
X x2;
std::cout << "x2 x->" << x2.x << std::endl;
return 0;
}
Post by Daniel Davidson
I hoped that if the compiler sees the initialization in a in the ctor it
does not also execute the one in the member definition.
If that were the case a second ctor could ignore Y altogether and still
get a nice default.
class X {
X({ Y y }) : y = y ?? new Y();
X.fromNothing();
Y y = new Y();
}
So in the first ctor the user may pass in a y and if so it is used, if
not one is allocated, but just once.
But in the fromNothing ctor the compiler understands no initialization
from the ctor and uses the class version.
I guess my mental model is if a member has an initializer in the class
and the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Post by 'Bob Nystrom' via Dart Misc
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?
– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups
"Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Daniel Davidson
2016-06-16 18:29:00 UTC
Permalink
Post by Daniel Davidson
y = y ?? new Y()
The first "y" is the field and its access causes the field initialiser to
run (which creates a Y).
Perhaps - but it does not have to. Why would you initialize it twice?
That is, why initialize it just to initialize/override it again with the
rhs?
Did the C++ analogue not clarify an alternative that might make more sense?
Post by Daniel Davidson
The second "y" is the constructor arg, which is null, so the final part is
run (which creates a Y).
Isn't this expected?
Post by Daniel Davidson
For example, this C++ analogue does not initialize x twice and exhibits
#include <iostream>
int getX()
{
std::cout << "Someone getting x" << std::endl;
return 42;
}
struct X {
X(int x) : x(x) {}
X() {}
int x = getX();
};
int main(int argc, char** argv) {
X x1(3);
std::cout << "x1 x->" << x1.x << std::endl;
X x2;
std::cout << "x2 x->" << x2.x << std::endl;
return 0;
}
Post by Daniel Davidson
I hoped that if the compiler sees the initialization in a in the ctor it
does not also execute the one in the member definition.
If that were the case a second ctor could ignore Y altogether and still
get a nice default.
class X {
X({ Y y }) : y = y ?? new Y();
X.fromNothing();
Y y = new Y();
}
So in the first ctor the user may pass in a y and if so it is used, if
not one is allocated, but just once.
But in the fromNothing ctor the compiler understands no initialization
from the ctor and uses the class version.
I guess my mental model is if a member has an initializer in the class
and the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Post by 'Bob Nystrom' via Dart Misc
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?
– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups
"Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Danny Tuppeny
2016-06-16 19:03:49 UTC
Permalink
Yeah, I understand what you mean but I would've been surprised if it did
what you're suggesting (I don't know if any languages I use would do that).
I don't know if it's a missed optimisation of a deliberate attempt to keep
the rules simpler.. The dart site says fields are "initialised on first
use"; I guess if they changes this that would have to be much more specific.

Maybe it's just as a result of originally being aimed at web devs (we're a
simple bunch) or having to easily translate to JS. I dunno :)
Post by Daniel Davidson
Post by Daniel Davidson
y = y ?? new Y()
The first "y" is the field and its access causes the field initialiser to
run (which creates a Y).
Perhaps - but it does not have to. Why would you initialize it twice?
That is, why initialize it just to initialize/override it again with the
rhs?
Did the C++ analogue not clarify an alternative that might make more sense?
Post by Daniel Davidson
The second "y" is the constructor arg, which is null, so the final part
is run (which creates a Y).
Isn't this expected?
For example, this C++ analogue does not initialize x twice and exhibits
Post by Daniel Davidson
Post by Daniel Davidson
#include <iostream>
int getX()
{
std::cout << "Someone getting x" << std::endl;
return 42;
}
struct X {
X(int x) : x(x) {}
X() {}
int x = getX();
};
int main(int argc, char** argv) {
X x1(3);
std::cout << "x1 x->" << x1.x << std::endl;
X x2;
std::cout << "x2 x->" << x2.x << std::endl;
return 0;
}
Post by Daniel Davidson
I hoped that if the compiler sees the initialization in a in the ctor
it does not also execute the one in the member definition.
If that were the case a second ctor could ignore Y altogether and still
get a nice default.
class X {
X({ Y y }) : y = y ?? new Y();
X.fromNothing();
Y y = new Y();
}
So in the first ctor the user may pass in a y and if so it is used, if
not one is allocated, but just once.
But in the fromNothing ctor the compiler understands no initialization
from the ctor and uses the class version.
I guess my mental model is if a member has an initializer in the class
and the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Post by 'Bob Nystrom' via Dart Misc
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?
– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to
http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google
Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups
"Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Daniel Davidson
2016-06-17 01:30:52 UTC
Permalink
I understand what you're saying, but first use could simply not consider the first occurrence of y in this (the lhs) as a 'usage' since it is not really such.

X(y) : y = y

It is really an initialization of y.
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
'Bob Nystrom' via Dart Misc
2016-06-16 19:08:30 UTC
Permalink
Post by Daniel Davidson
I guess my mental model is if a member has an initializer in the class and
the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Ah, I see.

That's not an unreasonable mental model, but it's not the one Dart has.
Since you wrote the code, it executes it. If you want it to be optional,
you would move it out of the field initializer and into the constructor
initialization list.
Post by Daniel Davidson
For example, this C++ analogue does not initialize x twice and exhibits
the behavior I was expecting
Huh, interesting. I didn't know C++ would do that. I think it may be the
case that C++ *has* to support that because of things like non-copyable
types. It may be that the double-initialization would fail to compile for
some field types otherwise.
Post by Daniel Davidson
y = y ?? new Y()
The first "y" is the field and its access causes the field initialiser to
run (which creates a Y).
Not exactly. Even if you don't mention the field in the constructor
initialization list, the field initializer still runs. (It would be
maddening if it didn't, since when you have a field initializer, the point
is usually so you don't have to initialize it in the constructor.)

It's just that the field initializers are specified to always run before
the constructor initialization list.

Cheers!

– bob
Post by Daniel Davidson
Post by Daniel Davidson
y = y ?? new Y()
The first "y" is the field and its access causes the field initialiser to
run (which creates a Y).
Perhaps - but it does not have to. Why would you initialize it twice?
That is, why initialize it just to initialize/override it again with the
rhs?
Did the C++ analogue not clarify an alternative that might make more sense?
Post by Daniel Davidson
The second "y" is the constructor arg, which is null, so the final part
is run (which creates a Y).
Isn't this expected?
Post by Daniel Davidson
For example, this C++ analogue does not initialize x twice and exhibits
#include <iostream>
int getX()
{
std::cout << "Someone getting x" << std::endl;
return 42;
}
struct X {
X(int x) : x(x) {}
X() {}
int x = getX();
};
int main(int argc, char** argv) {
X x1(3);
std::cout << "x1 x->" << x1.x << std::endl;
X x2;
std::cout << "x2 x->" << x2.x << std::endl;
return 0;
}
Post by Daniel Davidson
I hoped that if the compiler sees the initialization in a in the ctor
it does not also execute the one in the member definition.
If that were the case a second ctor could ignore Y altogether and still
get a nice default.
class X {
X({ Y y }) : y = y ?? new Y();
X.fromNothing();
Y y = new Y();
}
So in the first ctor the user may pass in a y and if so it is used, if
not one is allocated, but just once.
But in the fromNothing ctor the compiler understands no initialization
from the ctor and uses the class version.
I guess my mental model is if a member has an initializer in the class
and the ctor - the ctor should just be preferred.
The member initializer in the member definition is just for backup.
Post by 'Bob Nystrom' via Dart Misc
Post by Daniel Davidson
class Y {
Y() { print('Y ctor'); }
}
class X {
X({ Y y }) : y = y ?? new Y();
Y y = new Y();
}
main() {
new X();
}
What did you expect Dart to do here?
– bob
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to
http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google
Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups
"Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Danny Tuppeny
2016-06-16 19:18:23 UTC
Permalink
Post by Danny Tuppeny
The first "y" is the field and its access causes the field initialiser to
Post by Danny Tuppeny
run (which creates a Y).
Not exactly. Even if you don't mention the field in the constructor
initialization list, the field initializer still runs. (It would be
maddening if it didn't, since when you have a field initializer, the point
is usually so you don't have to initialize it in the constructor.)
It's just that the field initializers are specified to always run before
the constructor initialization list.
Ugh; I searched to see if that was the case (that's what I expected) before
replying and found something saying they were lazy. Just re-read it though
and seems that only applied to statics. Doh!
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Daniel Davidson
2016-06-17 01:23:51 UTC
Permalink
I think the C++ approach makes sense if you view it like this

1- any member may have a class instance initializer. Any ctor with uninitialized member invokes it.

2- ctor member initializer is used in place of any class member initializers

The benefit is when there are multiple named ctors you do not need to initialize defaults for each member for each ctor when there is a common class member default.

I'm bringing this up because I'm using code generation for dart classes based on data models and I was defaulting list and map members in both the class *default* ctor and in the ctor member initializer list, until I found it was double initializing.

Now I'll remove the class member initializers but each new ctor then needs to initialize all members which is heavy and unnecessary.
--
For other discussions, see https://groups.google.com/a/dartlang.org/

For HOWTO questions, visit http://stackoverflow.com/tags/dart

To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
Loading...