digitalmars.D.learn - problems with Rebindable
- chmike (68/68) May 21 2016 This thread is a followup of
- chmike (11/17) May 21 2016 Apparently Rebindable doesn't support polymorphism. This is
- ag0aep6g (15/28) May 21 2016 No, the difference in mutability is the problem. Can't implicitly
- ag0aep6g (2/3) May 21 2016 Rebindable!(immutable Info) x1, x2 = Infos.one;
- chmike (71/74) May 21 2016 Indeed. Thanks. Reading the unit tests in the source code and the
- Kagamin (3/8) May 21 2016 You can generate fairly unique ids and use them in switch
This thread is a followup of https://forum.dlang.org/post/vuljzyufphsywzevujhz forum.dlang.org with a refocused subject and question. I'm looking for a mutable reference to a none mutable object to implement the flyweight pattern. It is for a library and its user interface. So I'm not looking for hacks to get around the problem. Here is a toy example showing what I try to achieve and that fails to compile. The main shows what I expect users should be able to do. The aim is to allow the user to define another sets of constants of base type Info in his own class (e.g. MyInfos) and with a different implementation that the one provided in the example class Infos. ---- import std.stdio; import std.typecons; interface Info { } class Infos { static class Obj : Info { this(string msg) immutable { this.msg = msg; } private string msg; string toString() immutable { return msg; } } static immutable one = new immutable Obj("I'm one"); } void main() { Rebindable!Info x1, x2 = Infos.one; assert(x1 is null); assert(x1 == null); assert(x2 !is null); assert(x2 != null); assert(x2 is Infos.one); assert(x2 == Infos.one); x1 = x2; assert(x1 is x2); assert(x1 == x2); assert(x1 is Infos.one); assert(x1 == Infos.one); writeln(x1); switch(x1) { case Infos.one: writeln("case Infos.one"); break; default: writeln("default"); break; } // That is enough for today } ---- Compiling this code with dmd $ dmd --version DMD64 D Compiler v2.071.0 Copyright (c) 1999-2015 by Digital Mars written by Walter Bright I get the following errors: $ dub build Performing "debug" build using dmd for x86_64. testrebindable ~master: building configuration "application"... source/app.d(23,27): Error: cannot implicitly convert expression (one) of type immutable(Obj) to app.Info source/app.d(26,12): Error: use 'is' instead of '==' when comparing with null source/app.d(29,12): Error: use '!is' instead of '!=' when comparing with null source/app.d(43,5): Error: 'x1' must be of integral or string type, it is a app.Info source/app.d(45,10): Error: cannot implicitly convert expression (one) of type immutable(Obj) to app.Info dmd failed with exit code 1.
May 21 2016
On Saturday, 21 May 2016 at 10:42:13 UTC, chmike wrote:source/app.d(23,27): Error: cannot implicitly convert expression (one) of type immutable(Obj) to app.InfoApparently Rebindable doesn't support polymorphism. This is hopefully fixable.source/app.d(43,5): Error: 'x1' must be of integral or string type, it is a app.Info source/app.d(45,10): Error: cannot implicitly convert expression (one) of type immutable(Obj) to app.InfoA Rebindable variable can't be used as a switch argument. This would require a change to the language rules. However, the static immutable object Infos.one can be used as a case argument. The conclusion is that Rebindable doesn't cover the needs of a mutable object reference. In the flyweight pattern we only need to compare object addresses and we also want to use the lazy pattern to instantiate the immutable instances.
May 21 2016
On 05/21/2016 02:17 PM, chmike wrote:On Saturday, 21 May 2016 at 10:42:13 UTC, chmike wrote:No, the difference in mutability is the problem. Can't implicitly convert class objects from immutable to mutable. [...]source/app.d(23,27): Error: cannot implicitly convert expression (one) of type immutable(Obj) to app.InfoApparently Rebindable doesn't support polymorphism. This is hopefully fixable.A Rebindable variable can't be used as a switch argument. This would require a change to the language rules.Class objects in general can't be used as switch arguments.However, the static immutable object Infos.one can be used as a case argument.How do you figure that? I get 'Error: case must be a string or an integral constant, not Obj("I'm one")'.The conclusion is that Rebindable doesn't cover the needs of a mutable object reference.I don't follow. Seems to me that the limitations you see here are in place for ordinary class objects as well.In the flyweight pattern we only need to compare object addresses and we also want to use the lazy pattern to instantiate the immutable instances.It may be possible to get pointers from the objects and switch over those pointers. Can't use class objects directly, because equality is defined by opEquals which is virtual. And a switch doesn't make sense if opEquals needs to be called. Also, I'm afraid you're going to have a bad time when trying to get lazy initialization + immutable.
May 21 2016
On 05/21/2016 12:42 PM, chmike wrote:Rebindable!Info x1, x2 = Infos.one;Rebindable!(immutable Info) x1, x2 = Infos.one;
May 21 2016
On Saturday, 21 May 2016 at 13:17:11 UTC, ag0aep6g wrote:On 05/21/2016 12:42 PM, chmike wrote:Indeed. Thanks. Reading the unit tests in the source code and the implementation of Rebindable helped. Note however that it doesn't work with immutable. It only works with constant. I guess this is because immutable is "stronger" than const. I determined that only const was supported by looking at Rebindable's code. Here is the code that finally works as I want. The flyweight pattern is thus well supported with the exception that switch can't be used. using static functions to get the Infos.one also allow to implement lazy object instantiation. ---- import std.stdio; import std.typecons; interface IInfo { string toString() const; } // alias Rebindable!(immutable IInfo) Info; <-- Doesn't compile alias Rebindable!(const IInfo) Info; class Infos { static class Obj : IInfo { this(string msg) { this.msg = msg; } private string msg; override string toString() const { return msg; } } static Info one() { static auto x = Info(new Obj("I'm one")); return x; } static Info two() { static auto x = Info(new Obj("I'm two")); return x; } } void main() { Info x1; Info x2 = Infos.one; assert(x1 is null); assert(x2 !is null); assert(x2 is Infos.one); assert(x2 == Infos.one); x1 = x2; assert(x1 is x2); assert(x1 == x2); assert(x1 is Infos.one); assert(x1 == Infos.one); writeln(x1); Info x3 = Info(new Infos.Obj("I'm one")); assert(x1 !is x3); assert(x1 != x3); // Because there is no opEqual for deep equality test IInfo o1 = new Infos.Obj("I'm one"), o2 = new Infos.Obj("I'm one"); assert(o1 !is o2); assert(o1 != o2); // What I need for the flyweight pattern /* -- Doesn't compile : x1 is not a string or integral value switch(x1) { case Infos.one: writeln("case Infos.one"); break; default: writeln("default"); break; } */ } I wasn't indeed using Rebindable correctly and it support only const objects, not immutable objects. Thank you everybody for your help.Rebindable!Info x1, x2 = Infos.one;Rebindable!(immutable Info) x1, x2 = Infos.one;
May 21 2016
On 05/21/2016 03:36 PM, chmike wrote:Note however that it doesn't work with immutable. It only works with constant. I guess this is because immutable is "stronger" than const. I determined that only const was supported by looking at Rebindable's code. Here is the code that finally works as I want. The flyweight pattern is thus well supported with the exception that switch can't be used. using static functions to get the Infos.one also allow to implement lazy object instantiation.[...]I wasn't indeed using Rebindable correctly and it support only const objects, not immutable objects.I think your conclusion is wrong. Works fine if you add a couple `immutable`s (and change one `IInfo` to `Info`): ---- import std.stdio; import std.typecons; interface IInfo { string toString() const; } alias Rebindable!(immutable IInfo) Info; // Compiles just fine. class Infos { static class Obj : IInfo { this(string msg) immutable { this.msg = msg; } private string msg; override string toString() const { return msg; } } static Info one() { static auto x = Info(new immutable Obj("I'm one")); return x; } static Info two() { static auto x = Info(new immutable Obj("I'm two")); return x; } } void main() { Info x1; Info x2 = Infos.one; assert(x1 is null); assert(x2 !is null); assert(x2 is Infos.one); assert(x2 == Infos.one); x1 = x2; assert(x1 is x2); assert(x1 == x2); assert(x1 is Infos.one); assert(x1 == Infos.one); writeln(x1); Info x3 = Info(new immutable Infos.Obj("I'm one")); assert(x1 !is x3); assert(x1 != x3); // Because there is no opEqual for deep equality test Info o1 = new immutable Infos.Obj("I'm one"), o2 = new immutable Infos.Obj("I'm one"); assert(o1 !is o2); assert(o1 != o2); // What I need for the flyweight pattern /* -- Doesn't compile : x1 is not a string or integral value switch(x1) { case Infos.one: writeln("case Infos.one"); break; default: writeln("default"); break; } */ } ---- I thought marking the constructor `pure` would make it possible to implicitly convert a mutable `new` expression to immutable, but I couldn't get that to work. That would avoid all those `immutable`s. I'm probably forgetting something here.
May 21 2016
On Saturday, 21 May 2016 at 13:36:02 UTC, chmike wrote:static Info one() { static auto x = Info(new Obj("I'm one")); return x; } static Info two() { static auto x = Info(new Obj("I'm two")); return x; }FYI those are thread local variables.
May 21 2016
On Saturday, 21 May 2016 at 10:42:13 UTC, chmike wrote:switch(x1) { case Infos.one: writeln("case Infos.one"); break; default: writeln("default"); break; }You can generate fairly unique ids and use them in switch statements like this: https://dpaste.dzfl.pl/873b5b4cf71e
May 21 2016