www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to avoid inout type constructor with Optional type wrapper undoing

reply aliak <something something.com> writes:
Hi,

I'm playing around with an Optional wrapper type. It stores a 
type T and a bool that defines whether a value is defined or not:

struct Optional(T) {
   T value;
   bool defined = false;
   this(U : T)(auto ref inout(U) value) inout {
     this.value = value;
     this.defined = true;
   }
}

To facilitate it's use I have two type constructors:

inout(Optional!T) some(T)(auto ref inout(T) value) {
     return inout(Optional!T)(value);
}

Optional!T no(T)() {
     return Optional!T();
}

The above produces a problem when working with strings. Basically 
the type information gets slightly altered so you can't do this:

auto a = [no!string, some("hello")];

You get a type mismatch:

* no!string = Optional!string
* some("hello") = immutable(Optional!(char[]))

I've created a short code gist, so basically I'm wondering how to 
get it to compile without changing what's in main()

https://run.dlang.io/is/BreNdZ

I guess I can specialize on string type T, but this is a more 
general problem that can be shown with:

struct S {}
alias Thing = immutable S;
Thing thing = S();

auto x = some(thing);
auto y = no!Thing;
auto arr = [x, y]; // no can do buddy

Cheers,
- Ali
Jul 23 2018
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2018-07-23 20:39, aliak wrote:
 Hi,
 
 I'm playing around with an Optional wrapper type. It stores a type T and 
 a bool that defines whether a value is defined or not:
 
 struct Optional(T) {
    T value;
    bool defined = false;
    this(U : T)(auto ref inout(U) value) inout {
      this.value = value;
      this.defined = true;
    }
 }
 
 To facilitate it's use I have two type constructors:
 
 inout(Optional!T) some(T)(auto ref inout(T) value) {
      return inout(Optional!T)(value);
 }
 
 Optional!T no(T)() {
      return Optional!T();
 }
 
 The above produces a problem when working with strings. Basically the 
 type information gets slightly altered so you can't do this:
 
 auto a = [no!string, some("hello")];
 
 You get a type mismatch:
 
 * no!string = Optional!string
 * some("hello") = immutable(Optional!(char[]))
 
 I've created a short code gist, so basically I'm wondering how to get it 
 to compile without changing what's in main()
 
 https://run.dlang.io/is/BreNdZ
 
 I guess I can specialize on string type T, but this is a more general 
 problem that can be shown with:
 
 struct S {}
 alias Thing = immutable S;
 Thing thing = S();
 
 auto x = some(thing);
 auto y = no!Thing;
 auto arr = [x, y]; // no can do buddy
This [1] compiles the first example but not the second. [1] https://run.dlang.io/is/SJ02kP -- /Jacob Carlborg
Jul 23 2018
parent reply aliak <something something.com> writes:
On Monday, 23 July 2018 at 19:02:02 UTC, Jacob Carlborg wrote:
 This [1] compiles the first example but not the second.

 [1] https://run.dlang.io/is/SJ02kP
Aye it does, but it also sets T to always const which is unfortunately impractical for my use case :(
Jul 23 2018
parent reply aliak <something something.com> writes:
On Monday, 23 July 2018 at 19:22:13 UTC, aliak wrote:
 On Monday, 23 July 2018 at 19:02:02 UTC, Jacob Carlborg wrote:
 This [1] compiles the first example but not the second.

 [1] https://run.dlang.io/is/SJ02kP
Aye it does, but it also sets T to always const which is unfortunately impractical for my use case :(
Ok, now I'm totally confused. Defining an extra type constructor makes everything work. I.e add a const one to the inout one: auto defined(T)(const auto ref T value) { return W!T(value); } and everything works! Can anyone say why that is?
Jul 23 2018
parent aliak <something something.com> writes:
On Monday, 23 July 2018 at 19:31:42 UTC, aliak wrote:
 Ok, now I'm totally confused. Defining an extra type 
 constructor makes everything work. I.e add a const one to the 
 inout one:

 auto defined(T)(const auto ref T value) {
     return W!T(value);
 }

 and everything works!

 Can anyone say why that is?
Boh, seems other problems crop up now as doing this: auto a = defined!(int*)(null); produces error: onlineapp.d(13): Error: inout constructor onlineapp.W!(int*).W.__ctor!(int*).this creates const object, not mutable onlineapp.d(30): Error: template instance `onlineapp.defined!(int*)` error instantiating https://run.dlang.io/is/BWYxA8
Jul 23 2018
prev sibling next sibling parent reply Timoses <timosesu gmail.com> writes:
On Monday, 23 July 2018 at 18:39:59 UTC, aliak wrote:
 Hi,

 I'm playing around with an Optional wrapper type. It stores a 
 type T and a bool that defines whether a value is defined or 
 not:

 struct Optional(T) {
   T value;
   bool defined = false;
   this(U : T)(auto ref inout(U) value) inout {
     this.value = value;
     this.defined = true;
   }
 }

 To facilitate it's use I have two type constructors:

 inout(Optional!T) some(T)(auto ref inout(T) value) {
     return inout(Optional!T)(value);
 }

 Optional!T no(T)() {
     return Optional!T();
 }

 The above produces a problem when working with strings. 
 Basically the type information gets slightly altered so you 
 can't do this:

 auto a = [no!string, some("hello")];

 You get a type mismatch:

 * no!string = Optional!string
 * some("hello") = immutable(Optional!(char[]))

 I've created a short code gist, so basically I'm wondering how 
 to get it to compile without changing what's in main()

 https://run.dlang.io/is/BreNdZ

 I guess I can specialize on string type T, but this is a more 
 general problem that can be shown with:

 struct S {}
 alias Thing = immutable S;
 Thing thing = S();

 auto x = some(thing);
 auto y = no!Thing;
 auto arr = [x, y]; // no can do buddy

 Cheers,
 - Ali
I'm not being very helpful here, just throwing in more questions again: Why does this fail while it works when replacing T with U in struct W(T)?? It's so odd. Both T and U seem to resolve to "string". struct W(T) { const T value; // Replacing `T value` with `U value` compiles this(U : T)(auto ref const T value) { pragma(msg, T); // string pragma(msg, U); // string this.value = value; } } auto defined(T)(auto ref const T value) { return W!T(value); } void main() { auto a = defined("hello"); } Is this a bug?
Jul 24 2018
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/24/2018 02:47 AM, Timoses wrote:

 Why does this fail while it works when replacing T with U in struct
 W(T)?? It's so odd. Both T and U seem to resolve to "string".

      struct W(T) {
          const T value;
          // Replacing `T value` with `U value` compiles
          this(U : T)(auto ref const T value) {
That means, "any U that can implicitly be converted to string". However, when U does not appear in the function parameter list, there is no way for the compiler to deduce U. (I don't think there is syntax to specify constructor template parameters explicitly.) And if the parameter is always T, why is the constructor a template? Ok, perhaps U is used inside the constructor and the programmer needs to specify it... Still, I don't think there is such syntax. Ali
Jul 24 2018
parent Timoses <timosesu gmail.com> writes:
On Tuesday, 24 July 2018 at 14:11:51 UTC, Ali Çehreli wrote:
 On 07/24/2018 02:47 AM, Timoses wrote:

 Why does this fail while it works when replacing T with U in
struct
 W(T)?? It's so odd. Both T and U seem to resolve to "string".

      struct W(T) {
          const T value;
          // Replacing `T value` with `U value` compiles
          this(U : T)(auto ref const T value) {
That means, "any U that can implicitly be converted to string". However, when U does not appear in the function parameter list, there is no way for the compiler to deduce U. (I don't think there is syntax to specify constructor template parameters explicitly.) And if the parameter is always T, why is the constructor a template? Ok, perhaps U is used inside the constructor and the programmer needs to specify it... Still, I don't think there is such syntax. Ali
Ah, thanks!
Jul 25 2018
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/23/18 2:39 PM, aliak wrote:
 Hi,
 
 I'm playing around with an Optional wrapper type. It stores a type T and 
 a bool that defines whether a value is defined or not:
 
 struct Optional(T) {
    T value;
    bool defined = false;
    this(U : T)(auto ref inout(U) value) inout {
      this.value = value;
      this.defined = true;
    }
 }
Don't use inout here. The point of inout on the constructor is to *transfer* the mutability of the parameter to the struct instance. But you want to simply copy the type into the struct (an immutable(Optional!T) is quite useless, no?) Just use U, not inout(U), and don't put inout on the constructor. -Steve
Jul 27 2018
parent reply aliak <something something.com> writes:
On Friday, 27 July 2018 at 14:52:20 UTC, Steven Schveighoffer 
wrote:
 On 7/23/18 2:39 PM, aliak wrote:
 Hi,
 
 I'm playing around with an Optional wrapper type. It stores a 
 type T and a bool that defines whether a value is defined or 
 not:
 
 struct Optional(T) {
    T value;
    bool defined = false;
    this(U : T)(auto ref inout(U) value) inout {
      this.value = value;
      this.defined = true;
    }
 }
Don't use inout here. The point of inout on the constructor is to *transfer* the mutability of the parameter to the struct instance. But you want to simply copy the type into the struct (an immutable(Optional!T) is quite useless, no?) Just use U, not inout(U), and don't put inout on the constructor. -Steve
But then it only works for mutable Optional right? Why would an immutable(Optional!T) be useless? Data can be "forever" empty or a certain value.
Jul 28 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/28/18 6:06 PM, aliak wrote:
 On Friday, 27 July 2018 at 14:52:20 UTC, Steven Schveighoffer wrote:
 On 7/23/18 2:39 PM, aliak wrote:
 Hi,

 I'm playing around with an Optional wrapper type. It stores a type T 
 and a bool that defines whether a value is defined or not:

 struct Optional(T) {
    T value;
    bool defined = false;
    this(U : T)(auto ref inout(U) value) inout {
      this.value = value;
      this.defined = true;
    }
 }
Don't use inout here. The point of inout on the constructor is to *transfer* the mutability of the parameter to the struct instance. But you want to simply copy the type into the struct (an immutable(Optional!T) is quite useless, no?) Just use U, not inout(U), and don't put inout on the constructor.
But then it only works for mutable Optional right? Why would an immutable(Optional!T) be useless? Data can be "forever" empty or a certain value.
What I meant was that string is actually mutable (the data isn't mutable, but the string can be re-assigned to another one), so Optional!string is more useful than immutable(Optional!(char[])). I shouldn't have said that immutable(Optional!T) is useless, you are right, and it wouldn't make sense for the defined flag to change there anyway. But I see a problem here. string *is* mutable, yet, the parameter is accepted as inout(char[]). This seems unreasonable, I may want to preserve the head mutability. I didn't realize IFTI would do this. May be a bug, but I'm not certain. But you have it as auto ref, which means since you only sent in rvalues of strings, you didn't test the ref-ness of it. Indeed, if you do: string s = "hello"; auto a = defined(s); you get what you expected! This doesn't make a whole lot of sense. I wouldn't expect different types using IFTI this way. I'd recommend filing a bug on inout and IFTI. There isn't a good way around this... -Steve
Jul 29 2018
parent aliak <something something.com> writes:
On Sunday, 29 July 2018 at 12:30:58 UTC, Steven Schveighoffer 
wrote:
 On 7/28/18 6:06 PM, aliak wrote:
 [...]
What I meant was that string is actually mutable (the data isn't mutable, but the string can be re-assigned to another one), so Optional!string is more useful than immutable(Optional!(char[])). I shouldn't have said that immutable(Optional!T) is useless, you are right, and it wouldn't make sense for the defined flag to change there anyway. [...]
Ah right. So it seems inout is removing head qualifiers on by-val parameters? Filed what I think I understood from this: https://issues.dlang.org/show_bug.cgi?id=19125
Jul 29 2018