www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - lazy construction of an immutable object

reply "Puming" <zhaopuming gmail.com> writes:
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
parent reply "Puming" <zhaopuming gmail.com> writes:
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
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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
parent reply "Puming" <zhaopuming gmail.com> writes:
On Tuesday, 15 July 2014 at 13:59:24 UTC, Ali Çehreli wrote:
 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
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?
Jul 15 2014
parent reply "Meta" <jared771 gmail.com> writes:
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
parent reply "Puming" <zhaopuming gmail.com> writes:
On Tuesday, 15 July 2014 at 17:09:04 UTC, Meta wrote:
 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(); }
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?
Jul 15 2014
parent "Meta" <jared771 gmail.com> writes:
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
prev sibling parent "Jason den Dulk" <public2 jasondendulk.com> writes:
On Tuesday, 15 July 2014 at 12:20:57 UTC, Puming wrote:

  property immutable(T) freeze(T)(T obj)
 {
 	return cast(immutable(T))(obj);
 }
 What is the idiomatic approach to do this in D?
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.
 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