www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - overiding mutable methods in immutable classes

reply "Eric" <eric makechip.com> writes:
immutable class X
{
    private int x;
    this(int x) { this.x = x; }
    ...
    override size_t toHash(); // error: can't override mutable 
method
}

Since toHash() cannot be overridden in an immutable class,
is there a work-around?

In other words,

immutable X x1 = new immutable X(5);
immutable X x2 = new immutable X(5);

I would like for x1.toHash() to equal x2.toHash(),
but this is not the case because toHash() cannot be overridden.
Note also that (x1 != x2) even though they should be equal (I 
think...)
Is this a bug or a feature?

-Eric
Nov 21 2014
next sibling parent reply "anonymous" <anonymous example.com> writes:
On Saturday, 22 November 2014 at 00:24:39 UTC, Eric wrote:
 immutable class X
 {
    private int x;
    this(int x) { this.x = x; }
    ...
    override size_t toHash(); // error: can't override mutable 
 method
 }

 Since toHash() cannot be overridden in an immutable class,
 is there a work-around?

 In other words,

 immutable X x1 = new immutable X(5);
 immutable X x2 = new immutable X(5);

 I would like for x1.toHash() to equal x2.toHash(),
 but this is not the case because toHash() cannot be overridden.
A class qualifier is a short-hand to qualify all members of the class. That is, all fields and methods of an `immutable class` are immutable. That's problematic here, because an immutable method cannot overload a non-immutable method, and Object.toHash is not immutable. If you need to do override a non-immutable method, don't mark the whole class as immutable. Instead, mark everything individually. And make toHash const, not immutable. class X { private immutable int x; this(int x) immutable { this.x = x; } override size_t toHash() const {...} } But think about if enforced immutability is really what you want. You don't need to mark the fields immutable to be able to construct immutable Xs. A `pure` constructor is handy, as it can construct both mutable and immutable objects. class X { private int x; this(int x) pure { this.x = x; } } void main() { auto m = new X(5); auto i = new immutable X(5); }
 Note also that (x1 != x2) even though they should be equal (I 
 think...)
By default, equality of objects is defined as identity. That is, an Object is equal only to itself. Override opEquals to change that.
Nov 21 2014
parent reply "Eric" <eric makechip.com> writes:
 But think about if enforced immutability is really what you 
 want.
 You don't need to mark the fields immutable to be able to
 construct immutable Xs. A `pure` constructor is handy, as it can
 construct both mutable and immutable objects.

 class X
 {
      private int x;
      this(int x) pure { this.x = x; }
 }
 void main()
 {
      auto m = new X(5);
      auto i = new immutable X(5);
 }

 Note also that (x1 != x2) even though they should be equal (I 
 think...)
By default, equality of objects is defined as identity. That is, an Object is equal only to itself. Override opEquals to change that.
I know I can make a class immutable, but the problem is I want to constrain a template parameter to only immutable types, and I want to use class types. -Eric
Nov 21 2014
parent reply "anonymous" <anonymous example.com> writes:
On Saturday, 22 November 2014 at 02:37:21 UTC, Eric wrote:
 I know I can make a class immutable, but the problem is I want
 to constrain a template parameter to only immutable types,
 and I want to use class types.
template Foo(T : immutable Object) Accepts immutable(Object) and other immutable class types. Rejects non-immutable class types like Object and const(Object), and non-class types.
Nov 22 2014
parent reply "Eric" <eric makechip.com> writes:
On Saturday, 22 November 2014 at 09:57:55 UTC, anonymous wrote:
 On Saturday, 22 November 2014 at 02:37:21 UTC, Eric wrote:
 I know I can make a class immutable, but the problem is I want
 to constrain a template parameter to only immutable types,
 and I want to use class types.
template Foo(T : immutable Object) Accepts immutable(Object) and other immutable class types. Rejects non-immutable class types like Object and const(Object), and non-class types.
Yes, but if I don't declare the class T as immutable, I don't think this constraint will work. -Eric
Nov 22 2014
next sibling parent reply "anonymous" <anonymous example.com> writes:
On Saturday, 22 November 2014 at 15:00:00 UTC, Eric wrote:
 Yes, but if I don't declare the class T as immutable, I don't
 think this constraint will work.
You're mistaken. It works just fine. class X /* not immutable */ { private int x; this(int x) pure { this.x = x; } } template foo(T : immutable Object) { void foo(T thing) {} } void main() { foo(new immutable X(5)); }
Nov 22 2014
parent reply "Eric" <eric makechip.com> writes:
On Saturday, 22 November 2014 at 17:06:29 UTC, anonymous wrote:
 On Saturday, 22 November 2014 at 15:00:00 UTC, Eric wrote:
 Yes, but if I don't declare the class T as immutable, I don't
 think this constraint will work.
You're mistaken. It works just fine. class X /* not immutable */ { private int x; this(int x) pure { this.x = x; } } template foo(T : immutable Object) { void foo(T thing) {} } void main() { foo(new immutable X(5)); }
Yes, but this is what I really want: class X(T : immutable Object) { private T x; this(T x) pure { this.x = x; } } class Y { private int x; this(int x) pure { this.x = x; } } void main() { immutable(Y) y = new immutable Y(5); X!(Y) x = new X!(Y)(y); } // test.d(16): Error: template instance X!(Y) does not // match template declaration X(T : immutable(Object))
Nov 22 2014
parent reply "anonymous" <anonymous example.com> writes:
On Saturday, 22 November 2014 at 17:40:42 UTC, Eric wrote:
 Yes, but this is what I really want:

 class X(T : immutable Object)
 {
     private T x;
     this(T x) pure { this.x = x; }
 }

 class Y
 {
     private int x;
     this(int x) pure { this.x = x; }
 }

 void main()
 {
     immutable(Y) y = new immutable Y(5);
     X!(Y) x = new X!(Y)(y);
 }

 // test.d(16): Error: template instance X!(Y) does not
 // match template declaration X(T : immutable(Object))
I'm not sure what you're getting at. That is, I don't see which parts of the code you want to be as they are. For example, this works: class X(T /* optionally, restrict T to class types:*/ /*: const Object*/) { private T x; this(T x) pure { this.x = x; } } /* Maybe add a convenience construction function: */ X!T newX(T)(T x) {return new X!T(x);} class Y { private int x; this(int x) pure { this.x = x; } } void main() { immutable(Y) y = new immutable Y(5); X!(immutable Y) x = new X!(immutable Y)(y); auto x2 = newX(y); /* same as above, but less wordy */ } But I'm not sure if maybe I changed to much about it. My point is, that I think it's generally a good idea to be flexible when possible, and not make (im)mutability demands unless actually necessary. You may know the following, but I feel like there may be some confusion about it: Note that immutable(Y) is a different type from just Y. And declaring Y as `immutable class Y` wouldn't change a thing about that, as the "immutable" there only affects the members of Y. Objects of the bare type Y would still be (head) mutable.
Nov 22 2014
parent "Eric" <eric makechip.com> writes:
 But I'm not sure if maybe I changed to much about it.

 My point is, that I think it's generally a good idea to be
 flexible when possible, and not make (im)mutability demands
 unless actually necessary.

 You may know the following, but I feel like there may be some
 confusion about it: Note that immutable(Y) is a different type
 from just Y. And declaring Y as `immutable class Y` wouldn't
 change a thing about that, as the "immutable" there only affects
 the members of Y. Objects of the bare type Y would still be
 (head) mutable.
I thought I had my code working if instead of declaring "class Y", I declared "immutable class Y", but now it is not working for me. But finally this works: class X(T : immutable Object) { private T x; this(T x) pure { this.x = x; } } class Y { private const int x; this(int x) pure { this.x = x; } } void main() { immutable(Y) y = new immutable Y(4); X!(immutable(Y)) x = new X!(immutable(Y))(y); } Thanks for your help. -Eric
Nov 22 2014
prev sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, November 22, 2014 14:59:58 Eric via Digitalmars-d-learn wrote:
 On Saturday, 22 November 2014 at 09:57:55 UTC, anonymous wrote:
 On Saturday, 22 November 2014 at 02:37:21 UTC, Eric wrote:
 I know I can make a class immutable, but the problem is I want
 to constrain a template parameter to only immutable types,
 and I want to use class types.
template Foo(T : immutable Object) Accepts immutable(Object) and other immutable class types. Rejects non-immutable class types like Object and const(Object), and non-class types.
Yes, but if I don't declare the class T as immutable, I don't think this constraint will work.
There is no such thing as an immutable class in D. immutable class X { ... } is equivalent to immutable { class X { ... } } or class X { immutable { ... } } Classes themselves are never immutable or const - or even mutable. Now, _instances_ of classes can be const or immutable. So, if you have immutable X foo; then foo is a reference to an immutable instance of X, but X itself is not and never will be immutable. So, if you have template Foo(T : immutable Object) then what that says is that if an instance of the type being passed in would implicitly convert to immutable Object, it will be accepted. That means that Foo!(immutable Bar) will work so long as either Bar is a class, or it's a struct or class which implicitly converts to immutable Object via alias this. And no types which aren't qualified as immutable will work with Foo. However, in general, I'd suggest doing template Foo(T) if(is(T == immutable)) { } because that specifically constrains that the type given to Foo is qualified with immutable, avoiding any implicit conversion issues via alias this as well as working with types which aren't classes. And if you actually want T to be a class, you can do template Foo(T) if(is(T == immutable) && is(T == class)) { } It's more verbose, but it avoids Foo instantiating due to an implicit conversion via alias this. Regardless, you need to stop thinking of class declarations as being immutable or mutable or const. A class reference can be qualified with immutable or const so that the instance that it refers to is immutable or const, but classes themselves have nothing to do with mutability. Marking the reference as immutable immutable X foo; is what makes the type immutable not marking the class. immutable class X { } All marking the class as const or immutable is doing is qualifying all of its members as const or immutable, and that isn't going to work with any of the functions that are on Object itself (i.e. toString, toHash, opCmp, and opEquals) and is one of the reasons that we'd like to remove those functions from Object (letting subclasses then mark them with with whatever attributes are appropriate), but unfortunately, we have a ways to go before we can remove those functions from Object, since a number of things need to be done before we can do that (including fixing various compiler bugs and as well as the current AA implementation). - Jonathan M Davis
Nov 22 2014
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/21/14 7:24 PM, Eric wrote:
 immutable class X
 {
     private int x;
     this(int x) { this.x = x; }
     ...
     override size_t toHash(); // error: can't override mutable method
 }

 Since toHash() cannot be overridden in an immutable class,
 is there a work-around?
Kind of, but I don't think it's good that it works. Note that you shouldn't be able to call mutable toHash on an immutable object, but the runtime bludgeons it through: class X { override size_t toHash() {return x;} immutable: ... // everything else } This is equivalent to saying you have an immutable class, but just that one method is mutable. D lacks a mechanism to say something is mutable.
 In other words,

 immutable X x1 = new immutable X(5);
 immutable X x2 = new immutable X(5);

 I would like for x1.toHash() to equal x2.toHash(),
 but this is not the case because toHash() cannot be overridden.
 Note also that (x1 != x2) even though they should be equal (I think...)
 Is this a bug or a feature?
Classes are always references. This means you are comparing the class reference x1 to the class reference x2, and they point at different pieces of memory. You can override opEquals to define how to compare them, if you want != to work. I will note that opEquals *and* toHash must be overridden to work with AAs. -Steve
Nov 21 2014