digitalmars.D.learn - lazy construction of an immutable object
- Puming (25/25) Jul 15 2014 Hi,
- Puming (34/59) Jul 15 2014 I found another way to do this, namely first create a class that
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (25/58) Jul 15 2014 Also consider: A pure function's return value can implicitly be
- Puming (5/74) Jul 15 2014 wow, that's interesting :-) Is it the idiomatic approach to
- Jason den Dulk (6/13) Jul 15 2014 There is a Phobos function std.exception.assumeUnique which
Hi, I'd like to use immutable data, but instead of a one time constructor, I would like to `build` the data lazily, by setting its fields separately. In java version of protocol-buffer, there is a pattern for this mechanism: 1. Every data class in protobuf is immutable. 2. Each data class is companioned by a Builder class, with the same field of the immutable data class. 3. To create a data object, first create a Builder, then set the fields when ever you want. I want to emulate this process, but without needing to create two classes for one data class (protobuff does this by a separate code generating phase to generate both classes from a data format file). What is the idiomatic approach to do this in D? if I define a class with immutable fields, like ```d class A { immutable int id; immutable B b; } ``` should I use a template to generate the companion Builder class, or is there another aproach?
Jul 15 2014
I found another way to do this, namely first create a class that is mutable, then cast it to an immutable object before using it. ```d class A { int a; B b; this(int a, int b) { this.a = a; this.b = new B(b); } } class B { int b; this(int b) { this.b = b; } } property immutable(T) freeze(T)(T obj) { return cast(immutable(T))(obj); } void main() { immutable c = new A(3, 4).freeze; c.b.b = 5; // Error: can only initialize const member b inside constructor writeln(c.b.b) } ``` But the draw back is that you can't garanteed that the mutable object is never used. On Tuesday, 15 July 2014 at 10:39:42 UTC, Puming wrote:Hi, I'd like to use immutable data, but instead of a one time constructor, I would like to `build` the data lazily, by setting its fields separately. In java version of protocol-buffer, there is a pattern for this mechanism: 1. Every data class in protobuf is immutable. 2. Each data class is companioned by a Builder class, with the same field of the immutable data class. 3. To create a data object, first create a Builder, then set the fields when ever you want. I want to emulate this process, but without needing to create two classes for one data class (protobuff does this by a separate code generating phase to generate both classes from a data format file). What is the idiomatic approach to do this in D? if I define a class with immutable fields, like ```d class A { immutable int id; immutable B b; } ``` should I use a template to generate the companion Builder class, or is there another aproach?
Jul 15 2014
On 07/15/2014 05:20 AM, Puming wrote:I found another way to do this, namely first create a class that is mutable, then cast it to an immutable object before using it. ```d class A { int a; B b; this(int a, int b) { this.a = a; this.b = new B(b); } } class B { int b; this(int b) { this.b = b; } } property immutable(T) freeze(T)(T obj) { return cast(immutable(T))(obj); } void main() { immutable c = new A(3, 4).freeze; c.b.b = 5; // Error: can only initialize const member b inside constructor writeln(c.b.b) } ``` But the draw back is that you can't garanteed that the mutable object is never used.Also consider: A pure function's return value can implicitly be converted to immutable. class A { int x; int y; this (int x) pure { this. x = x; } } pure A makeA(int x) // <-- returns mutable { auto a = new A(x); // ... // Set a member later on: a.y = 42; return a; } void main() { immutable imm = makeA(1); // <-- works } Ali
Jul 15 2014
On Tuesday, 15 July 2014 at 13:59:24 UTC, Ali Çehreli wrote:On 07/15/2014 05:20 AM, Puming wrote:wow, that's interesting :-) Is it the idiomatic approach to initiate immutable objects lazily? Or do people use data class with immutable fields and generate a companion builder class at compile time?I found another way to do this, namely first create a class that is mutable, then cast it to an immutable object before using it. ```d class A { int a; B b; this(int a, int b) { this.a = a; this.b = new B(b); } } class B { int b; this(int b) { this.b = b; } } property immutable(T) freeze(T)(T obj) { return cast(immutable(T))(obj); } void main() { immutable c = new A(3, 4).freeze; c.b.b = 5; // Error: can only initialize const member b inside constructor writeln(c.b.b) } ``` But the draw back is that you can't garanteed that the mutable object is never used.Also consider: A pure function's return value can implicitly be converted to immutable. class A { int x; int y; this (int x) pure { this. x = x; } } pure A makeA(int x) // <-- returns mutable { auto a = new A(x); // ... // Set a member later on: a.y = 42; return a; } void main() { immutable imm = makeA(1); // <-- works } Ali
Jul 15 2014
On Tuesday, 15 July 2014 at 15:48:10 UTC, Puming wrote:wow, that's interesting :-) Is it the idiomatic approach to initiate immutable objects lazily? Or do people use data class with immutable fields and generate a companion builder class at compile time?There's no real idiomatic approach, I think, because this is somewhat unexplored territory. I'd say constructing the object inside a pure method so it can be implicitly cast to immutable is more typesafe, but if you're storing the immutable reference to the object in a class or struct, it can only be initialized in that class's/struct's constructor. So the following isn't possible: struct Test { immutable(int*) n; private int* getPtr() pure { int* n = new int; *n = 42; return n; } public void initialize() pure { n = getPtr(); } } void main() { auto t = new Test(); test.initialize(); }
Jul 15 2014
On Tuesday, 15 July 2014 at 17:09:04 UTC, Meta wrote:On Tuesday, 15 July 2014 at 15:48:10 UTC, Puming wrote:So this means that the pure method approach should also be transitive: the containing class should also be mutable (having the desired immutable object as a mutable field) and later freeze itself. If there is a complex hierarchy of objects, there are three strategies: 1. define all classes and fields mutable, and use immutable value in the business logic code. This would work, but lose the compilers help with checking immutability in all conditions. For example, you coud modify the mutable reference if it is leaked. 2. define all classes and fields immutable, and only use constructor to eagerly create objects and there fields. This is the most strict, but will incur some inconvenience in asynchronous environments, where not all parts of the data is available at start. 3. define all classes and use template magic to generate companion builders just like protobuffer does. But that would be complicated and I don't know how to do it. Do you have any suggestion for this approach?wow, that's interesting :-) Is it the idiomatic approach to initiate immutable objects lazily? Or do people use data class with immutable fields and generate a companion builder class at compile time?There's no real idiomatic approach, I think, because this is somewhat unexplored territory. I'd say constructing the object inside a pure method so it can be implicitly cast to immutable is more typesafe, but if you're storing the immutable reference to the object in a class or struct, it can only be initialized in that class's/struct's constructor. So the following isn't possible: struct Test { immutable(int*) n; private int* getPtr() pure { int* n = new int; *n = 42; return n; } public void initialize() pure { n = getPtr(); } } void main() { auto t = new Test(); test.initialize(); }
Jul 15 2014
On Wednesday, 16 July 2014 at 00:38:46 UTC, Puming wrote:3. define all classes and use template magic to generate companion builders just like protobuffer does. But that would be complicated and I don't know how to do it. Do you have any suggestion for this approach?This would probably be a good solution, but it will be a bit tricky to generate the builder classes. Certainly doable. Take a look at std.traits: FieldTypeTuple and RepresentationTypeTuple, and the built in __traits(allMembers, <symbol>).
Jul 16 2014
On Tuesday, 15 July 2014 at 12:20:57 UTC, Puming wrote:property immutable(T) freeze(T)(T obj) { return cast(immutable(T))(obj); }There is a Phobos function std.exception.assumeUnique which performs this for arrays. According to the docs, it does what 'freeze' does, and is considered idiomatic.What is the idiomatic approach to do this in D?But the draw back is that you can't garanteed that the mutable object is never used.I got the impression that assumeUnique has the same problem. Regards
Jul 15 2014