www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Alias type with different initialiser.

reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
Hi,

In Extended Pascal, you can derive from a basic type and change 
the default initialiser like so:

   type int1 = integer value 1;
   var i : int1;
       ii : int1 value 2;
   assert(i = 1);
   assert(ii = 2);

I have it working in D, but it seems a little clumsy. Is there a 
better way?


   struct initial(T, T val)
   {
       private T _payload = val;
       alias _payload this;

       static initial opCall(T v)
       {
           initial s;
           s._payload = v;
           return s;
       }
   }

   unittest
   {
       alias initial!(int, 1) int1;
       int1 i;
       assert(i == 1);
       int1 ii = 2;
       assert(ii == 2);
   }
Feb 13
next sibling parent reply Daniel Kozak via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
Dne 13.2.2017 v 16:28 Bastiaan Veelo via Digitalmars-d-learn napsal(a):

 Hi,

 In Extended Pascal, you can derive from a basic type and change the 
 default initialiser like so:

   type int1 = integer value 1;
   var i : int1;
       ii : int1 value 2;
   assert(i = 1);
   assert(ii = 2);

 I have it working in D, but it seems a little clumsy. Is there a 
 better way?


   struct initial(T, T val)
   {
       private T _payload = val;
       alias _payload this;

       static initial opCall(T v)
       {
           initial s;
           s._payload = v;
           return s;
       }
   }

   unittest
   {
       alias initial!(int, 1) int1;
       int1 i;
       assert(i == 1);
       int1 ii = 2;
       assert(ii == 2);
   }
https://dlang.org/phobos/std_typecons.html#.Typedef
Feb 13
parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Monday, 13 February 2017 at 16:40:02 UTC, Daniel Kozak wrote:
 https://dlang.org/phobos/std_typecons.html#.Typedef
Thanks for the pointers. Both Typedef and Proxy create types that don't mix with the base type, which I want to the contrary. So I guess I'll go with struct Initial(T, T val) { private T _payload = val; alias _payload this; static Initial opCall(T v) { Initial s; s._payload = v; return s; } static T init() { return val; } } unittest { alias Initial!(int, 1) int1; int1 i; assert(i == 1); int1 ii = 2; assert(ii == 2); assert(ii.init == 1); assert(int1.init == 1); void f(int val) { assert(val == 1); } f(i); int i0; assert(i0 == 0); i = i0; assert(i == 0); assert(i.init == 1); i0 = ii; assert(i0 == 2); assert(i0.init == 0); }
Feb 13
parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Monday, 13 February 2017 at 22:16:36 UTC, Bastiaan Veelo wrote:
 On Monday, 13 February 2017 at 16:40:02 UTC, Daniel Kozak wrote:
 https://dlang.org/phobos/std_typecons.html#.Typedef
Thanks for the pointers. Both Typedef and Proxy create types that don't mix with the base type, which I want to the contrary. So I guess I'll go with
Why not use a constructor instead of static opCall? Also, it's generally a bad idea to define `.init` for any type as code generally expects this to be the compiler-generated property (e.g. a value of type Initial!(int, 1) not of type int). So, perhaps like this: struct Initial(T, T val) { private T _payload = val; alias _payload this; this(T v) { _payload = v; } enum initial = val; } unittest { alias Initial!(int, 1) int1; static assert(int1.initial == 1); // typeof(int1.initial) == int static assert(int1.init == 1); // typeof(int1.init) == typeof(int1) int1 i; assert(i == 1); int1 ii = 2; assert(ii == 2); assert(ii.init == 1); assert(int1.init == 1); void f(int val) { assert(val == 1); } f(i); int i0; assert(i0 == 0); i = i0; assert(i == 0); assert(i.init == 1); i0 = ii; assert(i0 == 2); assert(i0.init == 0); }
Feb 13
next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote:
 [ snip ]
sorry, made a typo, that should have been
     alias int1 = Initial!(int, 1);
     static assert(int1.initial == 1); // typeof(int1.initial) 
 == int
     static assert(int1.init == 1); // typeof(int1.init) == int1
Feb 13
parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Tuesday, 14 February 2017 at 01:31:10 UTC, John Colvin wrote:
 On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote:
 [ snip ]
sorry, made a typo, that should have been
     alias int1 = Initial!(int, 1);
     static assert(int1.initial == 1); // typeof(int1.initial) 
 == int
     static assert(int1.init == 1); // typeof(int1.init) == int1
What is the difference between alias Initial!(int, 1) int1; and alias int1 = Initial!(int, 1); ? Or was the typo in the comments alone? Thanks.
Feb 14
parent John Colvin <john.loughran.colvin gmail.com> writes:
On Tuesday, 14 February 2017 at 10:49:19 UTC, Bastiaan Veelo 
wrote:
 On Tuesday, 14 February 2017 at 01:31:10 UTC, John Colvin wrote:
 On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote:
 [ snip ]
sorry, made a typo, that should have been
     alias int1 = Initial!(int, 1);
     static assert(int1.initial == 1); // typeof(int1.initial) 
 == int
     static assert(int1.init == 1); // typeof(int1.init) == 
 int1
What is the difference between alias Initial!(int, 1) int1; and alias int1 = Initial!(int, 1); ? Or was the typo in the comments alone? Thanks.
just a more modern style. I think the old style would have been deprecated if it wasn't for how much old code used it.
Feb 14
prev sibling next sibling parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote:
 Why not use a constructor instead of static opCall?
I don't know, this comes from http://dlang.org/spec/struct.html#dynamic_struct_init. Your constructor looks a lot better. Am I missing a test case where static opCall would be called, but not the constructor? Why do the docs use static opCall?
 Also, it's generally a bad idea to define `.init` for any type 
 as code generally expects this to be the compiler-generated 
 property (e.g. a value of type Initial!(int, 1) not of type 
 int).
Thanks. I can't remember what confused me to think that typeof(int1.init) had to be int.
     enum initial = val;
[...]
     static assert(int1.initial == 1); // typeof(int1.initial) 
 == int
These lines have no purpose beyond illustration, right? I now have the following, featuring the novel Scherkl-Nielsen self-important lookup: /** Creates an type that is mostly $(PARAM T), only with a different initial value of $(PARAM val). */ struct Initial(T, T val) { private T _payload = val; alias _payload this; this(T v) { _payload = v; } // https://dlang.org/blog/2017/02/13/a-new-import-idiom/ private template from(string moduleName) { mixin("import from = " ~ moduleName ~ ";"); } void toString(scope void delegate(const(char)[]) sink, from!"std.format".FormatSpec!char fmt) { import std.array : appender; import std.format : formatValue; auto w = appender!string(); formatValue(w, _payload, fmt); sink(w.data); } } unittest { alias int1 = Initial!(int, 1); static assert(int1.init == 1); // typeof(int1.init) == int1 int1 i; assert(i == 1); int1 ii = 2; assert(ii == 2); assert(ii.init == 1); assert(int1.init == 1); void f(int val) { assert(val == 1); } f(i); int i0; assert(i0 == 0); i = i0; assert(i == 0); assert(i.init == 1); i0 = ii; assert(i0 == 2); assert(i0.init == 0); import std.string; assert(format("%6d", ii) == " 2"); }
Feb 14
parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Tuesday, 14 February 2017 at 11:34:22 UTC, Bastiaan Veelo 
wrote:
 On Monday, 13 February 2017 at 22:59:11 UTC, John Colvin wrote:
 Why not use a constructor instead of static opCall?
I don't know, this comes from http://dlang.org/spec/struct.html#dynamic_struct_init. Your constructor looks a lot better. Am I missing a test case where static opCall would be called, but not the constructor? Why do the docs use static opCall?
The docs are just trying to illustrate that opCall is only used for initialisation if the initialiser is of a different type. Constructors are always used for initialisation if they are there, they completely hide static opCall in that context. static opCall can return anything of any type, constructors implicitly return a reference to `this`. A constructor is the way to initialise a type and some code - such as std.conv.emplace - actually calls the constructor manually (it's accessible via .__ctor) to do so. static opCall doesn't necessarily represent an initialiser function, so generic code won't necessarily know to use it. Overall, static opCall is rarely the solution you need.
 Also, it's generally a bad idea to define `.init` for any type 
 as code generally expects this to be the compiler-generated 
 property (e.g. a value of type Initial!(int, 1) not of type 
 int).
Thanks. I can't remember what confused me to think that typeof(int1.init) had to be int.
     enum initial = val;
[...]
     static assert(int1.initial == 1); // typeof(int1.initial) 
 == int
These lines have no purpose beyond illustration, right?
they check that the implementation does have a member .initial and that it is equal to the value we requested as the initialiser. It also enforces that it is accessible at compile-time.
 I now have the following, featuring the novel Scherkl-Nielsen 
 self-important lookup:

 /**
 Creates an type that is mostly $(PARAM T), only with a 
 different initial value of $(PARAM val).
 */
 struct Initial(T, T val)
 {
     private T _payload = val;
     alias _payload this;

     this(T v)
     {
         _payload = v;
     }

     // https://dlang.org/blog/2017/02/13/a-new-import-idiom/
     private template from(string moduleName)
     {
       mixin("import from = " ~ moduleName ~ ";");
     }

     void toString(scope void delegate(const(char)[]) sink, 
 from!"std.format".FormatSpec!char fmt)
     {
         import std.array : appender;
         import std.format : formatValue;
         auto w = appender!string();
         formatValue(w, _payload, fmt);
         sink(w.data);
     }
 }

 unittest
 {
     alias int1 = Initial!(int, 1);
     static assert(int1.init == 1); // typeof(int1.init) == int1

     int1 i;
     assert(i == 1);
     int1 ii = 2;
     assert(ii == 2);
     assert(ii.init == 1);
     assert(int1.init == 1);

     void f(int val)
     {
         assert(val == 1);
     }
     f(i);

     int i0;
     assert(i0 == 0);
     i = i0;
     assert(i == 0);
     assert(i.init == 1);
     i0 = ii;
     assert(i0 == 2);
     assert(i0.init == 0);

     import std.string;
     assert(format("%6d", ii) == "     2");
 }
I would recommend making `template from(string moduleName)` global (maybe in a utils module?), there's no reason for it to be a member of Initial.
Feb 14
parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
Thanks again.

On Tuesday, 14 February 2017 at 14:08:31 UTC, John Colvin wrote:
 I would recommend making `template from(string moduleName)` 
 global (maybe in a utils module?), there's no reason for it to 
 be a member of Initial.
Yes, I think there is high chance it will be part of Phobos, if chosen in favour of DIP 1005.
Feb 14
prev sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, February 13, 2017 22:59:11 John Colvin via Digitalmars-d-learn 
wrote:
 Also, it's
 generally a bad idea to define `.init` for any type as code
 generally expects this to be the compiler-generated property
 (e.g. a value of type Initial!(int, 1) not of type int).
And there's actually a decent chance that at some point, it will become illegal precisely because of the problems that it causes to allow it. - Jonathan M Davis
Feb 14
prev sibling parent Daniel Kozak via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
Dne 13.2.2017 v 17:40 Daniel Kozak napsal(a):

 Dne 13.2.2017 v 16:28 Bastiaan Veelo via Digitalmars-d-learn napsal(a):

 Hi,

 In Extended Pascal, you can derive from a basic type and change the 
 default initialiser like so:

   type int1 = integer value 1;
   var i : int1;
       ii : int1 value 2;
   assert(i = 1);
   assert(ii = 2);

 I have it working in D, but it seems a little clumsy. Is there a 
 better way?


   struct initial(T, T val)
   {
       private T _payload = val;
       alias _payload this;

       static initial opCall(T v)
       {
           initial s;
           s._payload = v;
           return s;
       }
   }

   unittest
   {
       alias initial!(int, 1) int1;
       int1 i;
       assert(i == 1);
       int1 ii = 2;
       assert(ii == 2);
   }
https://dlang.org/phobos/std_typecons.html#.Typedef
or you can use Proxy https://dlang.org/phobos/std_typecons.html#.Proxy struct initial(T, T val) { private T _payload = val; mixin Proxy!_payload; this(T v) { _payload = v; } } unittest { alias initial!(int, 1) int1; int1 i; assert(i == 1); int1 ii = 2; assert(ii == 2); }
Feb 13