www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Construct immutable member in derived class

reply Timoses <timosesu gmail.com> writes:
Example:

```
class A
{
     immutable int i;

     this(){}
}

class B : A
{
     this()
     {
      	this.i = 3;
     }
}

void main()
{
     auto b = new B;
}
```

throws:
 Error: constructor `onlineapp.A.this` missing initializer for 
 immutable field i
 Error: cannot modify immutable expression this.i
Why can't I initialize the immutable member in the derived class?
Apr 04 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 4 April 2018 at 10:11:37 UTC, Timoses wrote:
 Example:

 ```
 class A
 {
     immutable int i;

     this(){}
 }

 class B : A
 {
     this()
     {
      	this.i = 3;
     }
 }

 void main()
 {
     auto b = new B;
 }
 ```

 throws:
 Error: constructor `onlineapp.A.this` missing initializer for 
 immutable field i
 Error: cannot modify immutable expression this.i
Why can't I initialize the immutable member in the derived class?
Because by the time B's constructor is called, A might already have initialized it, and rely on it never changing. The solution is to add a constructor overload to A, and call that from B: class A { immutable int i; protected this(int i) { this.i = i; } } class B : A { this() { super(3); } } unittest { auto b = new B; } -- Simen
Apr 04 2018
parent reply Timoses <timosesu gmail.com> writes:
On Wednesday, 4 April 2018 at 10:41:52 UTC, Simen Kjærås wrote:
 Because by the time B's constructor is called, A might already 
 have initialized it, and rely on it never changing.
What about: ``` class A { immutable int i; this(){} } class B : A { this() { this.i = 3; super(); // <- specifically calling // super constructor afterwards } } void main() { auto b = new B; } ``` Same result
 The solution is to add a constructor overload to A, and call 
 that from B:

 class A
 {
     immutable int i;

     protected this(int i) {
         this.i = i;
     }
 }

 class B : A
 {
     this()
     {
         super(3);
     }
 }

 unittest
 {
     auto b = new B;
 }

 --
   Simen
This becomes a bit hideous, unfortunately, when there are many initializations involved. Found this, but it doesn't mention anything about derived classes.. https://dlang.org/spec/class.html#field-init
Apr 04 2018
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Wednesday, 4 April 2018 at 16:05:52 UTC, Timoses wrote:
 This becomes a bit hideous, unfortunately, when there are many 
 initializations involved.

 Found this, but it doesn't mention anything about derived 
 classes..
 https://dlang.org/spec/class.html#field-init
Here is something: https://dlang.org/spec/class.html#constructors By the rules 7 and 8 it is suggested, what Simen already said, the construction of the base class can be independent from the derived one. And as such, the immutability has to handled in the constructor, next to the variable...
Apr 04 2018
parent reply Timoses <timosesu gmail.com> writes:
On Wednesday, 4 April 2018 at 16:16:24 UTC, Alex wrote:
 Here is something:
 https://dlang.org/spec/class.html#constructors

 By the rules 7 and 8 it is suggested, what Simen already said, 
 the construction of the base class can be independent from the 
 derived one. And as such, the immutability has to handled in 
 the constructor, next to the variable...
"[...] the construction of the base class can be independent from the derived one." Hm, the points 7 and 8 don't clearly state what you wrote. But it somehow does make sense.. Still I wonder why that is so. Let's say you have an abstract class with immutable members. Why shouldn't derived class constructors be allowed to initialize these immutable members?
Apr 04 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Wednesday, 4 April 2018 at 21:49:08 UTC, Timoses wrote:
 "[...] the construction of the base class can be independent 
 from the derived one."

 Hm, the points 7 and 8 don't clearly state what you wrote.
Yes :)
 But it somehow does make sense.. Still I wonder why that is so.

 Let's say you have an abstract class with immutable members. 
 Why shouldn't derived class constructors be allowed to 
 initialize these immutable members?
My reason is a semantic one, so it's rather how I'm explaining this to me, then a formal one. Let's assume, we abstract away a member from different derived classes to an abstract class, which cannot be handled by this abstract class. Ok, this can happen, so it would be ok, if the base class does not handle the var. But then, we add a qualifier. How can this be? As the abstract class cannot handle the abstraction, it also can not add any qualifiers. As the qualifier is there, then, the base class at least has knowledge about the immutability. And due this fact, its own constructor has to handle this variable in some way. However, this a minor problem, isn't it? If you argue, that you can also abstract the immutability qualifier, then, I would say, that a derived class always has a better knowledge how to handle its members, and therefore how to call the base class constructor, as it knows which class it is derived from. So, for short: Either: the immutability belongs to the base class and therefore it has to manage the var Or: it doesn't and the derived classes have the knowledge how to serve their base class.
Apr 05 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, April 05, 2018 13:36:07 Alex via Digitalmars-d-learn wrote:
 On Wednesday, 4 April 2018 at 21:49:08 UTC, Timoses wrote:
 "[...] the construction of the base class can be independent
 from the derived one."

 Hm, the points 7 and 8 don't clearly state what you wrote.
Yes :)
 But it somehow does make sense.. Still I wonder why that is so.

 Let's say you have an abstract class with immutable members.
 Why shouldn't derived class constructors be allowed to
 initialize these immutable members?
My reason is a semantic one, so it's rather how I'm explaining this to me, then a formal one. Let's assume, we abstract away a member from different derived classes to an abstract class, which cannot be handled by this abstract class. Ok, this can happen, so it would be ok, if the base class does not handle the var. But then, we add a qualifier. How can this be? As the abstract class cannot handle the abstraction, it also can not add any qualifiers. As the qualifier is there, then, the base class at least has knowledge about the immutability. And due this fact, its own constructor has to handle this variable in some way. However, this a minor problem, isn't it? If you argue, that you can also abstract the immutability qualifier, then, I would say, that a derived class always has a better knowledge how to handle its members, and therefore how to call the base class constructor, as it knows which class it is derived from. So, for short: Either: the immutability belongs to the base class and therefore it has to manage the var Or: it doesn't and the derived classes have the knowledge how to serve their base class.
The reality of the matter is that a member variable is a member of the class that it's in and _must_ be handled by that class. If it's not directly initialized in its declaration or initialized in that class' constructor, then it gets a default value. Derived classes have _zero_ control over that. They can mutate a public or protected mutable member of a base class, but that member variable still has to be initialized by the base class, not the derived class. And you can't abstract whether a member variable is marked with immutable or not. That's part of the variable. Declaring an immutable instance of an object would then treat the member variable in immutable for that instance, so it's possible to have an immutable member variable when the member variable is not itself marked with immutable, but class inheritance has nothing to do with controlling which type qualifiers were used on the member variable of a base class. Derived classes override member functions. They don't override member variables or anything about them. They override the behavior of the base class, not the structure. For them to do otherwise would become quite problematic if you ever use a derived class through a base class reference. They can add onto the structure of a base class, but the derived class must be usuable via a base class reference, and trying to do something like have the derived class alter the qualifiers on base class member variable simply would not work with that. That sort of thing could only ever work if the base class were just a way to add functionality to a derived class rather than having anything to do with references, and that's simply not how classes work in D. If that's the sort of thing that you want, it would make more sense to add the functionality via composition rather than inheritance. - Jonathan M Davis
Apr 05 2018
parent Alex <sascha.orlov gmail.com> writes:
On Thursday, 5 April 2018 at 19:31:39 UTC, Jonathan M Davis wrote:
 And you can't abstract whether a member variable is marked with 
 immutable or not. That's part of the variable. Declaring an 
 immutable instance of an object would then treat the member 
 variable in immutable for that instance, so it's possible to 
 have an immutable member variable when the member variable is 
 not itself marked with immutable, but class inheritance has 
 nothing to do with controlling which type qualifiers were used 
 on the member variable of a base class. Derived classes 
 override member functions. They don't override member variables 
 or anything about them. They override the behavior of the base 
 class, not the structure.
That beats my argumentation. Fully agree with that.
 For them to do otherwise would become quite problematic if you 
 ever use a derived class through a base class reference. They 
 can add onto the structure of a base class, but the derived 
 class must be usuable via a base class reference, and trying to 
 do something like have the derived class alter the qualifiers 
 on base class member variable simply would not work with that. 
 That sort of thing could only ever work if the base class were 
 just a way to add functionality to a derived class rather than 
 having anything to do with references, and that's simply not 
 how classes work in D. If that's the sort of thing that you 
 want, it would make more sense to add the functionality via 
 composition rather than inheritance.
Yeah... little bit of mixing things up in my head, maybe... Did exactly this stuff during the last couple of months. Thanks a lot!
Apr 05 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, April 04, 2018 16:05:52 Timoses via Digitalmars-d-learn wrote:
 On Wednesday, 4 April 2018 at 10:41:52 UTC, Simen Kjærås wrote:
 Because by the time B's constructor is called, A might already
 have initialized it, and rely on it never changing.
What about: ``` class A { immutable int i; this(){} } class B : A { this() { this.i = 3; super(); // <- specifically calling // super constructor afterwards } } void main() { auto b = new B; } ```
That code doesn't compile - at least not with dmd master. It gives these two errors: q.d(5): Error: constructor `q.A.this` missing initializer for immutable field i q.d(12): Error: cannot modify immutable expression this.i So, it's an error that the base class doesn't initialize the immutable member, and it's an error for the derived class to try to assign to it. - Jonathan M Davis
Apr 04 2018
parent reply Timoses <timosesu gmail.com> writes:
On Wednesday, 4 April 2018 at 18:11:12 UTC, Jonathan M Davis 
wrote:
 On Wednesday, April 04, 2018 16:05:52 Timoses via
 ```
 class A
 {
      immutable int i;

      this(){}
 }

 class B : A
 {
      this()
      {
           this.i = 3;
          super();              // <- specifically calling
                                //    super constructor 
 afterwards
      }
 }

 void main()
 {
   auto b = new B;
 }
 ```
That code doesn't compile - at least not with dmd master. It gives these two errors: q.d(5): Error: constructor `q.A.this` missing initializer for immutable field i q.d(12): Error: cannot modify immutable expression this.i So, it's an error that the base class doesn't initialize the immutable member, and it's an error for the derived class to try to assign to it. - Jonathan M Davis
I know, should have mentioned it. The question is why, however? A rule like the above would force all derived classes to initialize the immutable variable from the base class (if they were allowed to). Why aren't they?
Apr 04 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, April 04, 2018 21:46:13 Timoses via Digitalmars-d-learn wrote:
 On Wednesday, 4 April 2018 at 18:11:12 UTC, Jonathan M Davis
 That code doesn't compile - at least not with dmd master. It
 gives these two errors:

 q.d(5): Error: constructor `q.A.this` missing initializer for
 immutable
 field i
 q.d(12): Error: cannot modify immutable expression this.i

 So, it's an error that the base class doesn't initialize the
 immutable member, and it's an error for the derived class to
 try to assign to it.
I know, should have mentioned it. The question is why, however? A rule like the above would force all derived classes to initialize the immutable variable from the base class (if they were allowed to). Why aren't they?
Because doing that basically makes it impossible to guarantee that the type system isn't violated. Once an immutable variable has been initialized, its value must _never_ change. It must be initalized exactly once, and the compiler doesn't necessarily have any clue what the base class constructors did or didn't do. There's no guarantee that it even has access to the function bodies for the base class when compiling the derived class. So, there is no way for it to safely put off the initialization of any base class members for the derived class to do. - Jonathan M Davis
Apr 04 2018
parent Timoses <timosesu gmail.com> writes:
On Wednesday, 4 April 2018 at 22:47:07 UTC, Jonathan M Davis 
wrote:
 Because doing that basically makes it impossible to guarantee 
 that the type system isn't violated. Once an immutable variable 
 has been initialized, its value must _never_ change. It must be 
 initalized exactly once, and the compiler doesn't necessarily 
 have any clue what the base class constructors did or didn't 
 do. There's no guarantee that it even has access to the 
 function bodies for the base class when compiling the derived 
 class. So, there is no way for it to safely put off the 
 initialization of any base class members for the derived class 
 to do.

 - Jonathan M Davis
Ah, makes sense. I was looking for a somewhat technical answer. Thanks for that : ).
Apr 05 2018