Jump to page: 1 2
Thread overview
How to avoid inout type constructor with Optional type wrapper undoing string type
Jul 23, 2018
aliak
Jul 23, 2018
Jacob Carlborg
Jul 23, 2018
aliak
Jul 23, 2018
aliak
Jul 23, 2018
aliak
Jul 24, 2018
Timoses
Jul 24, 2018
Ali Çehreli
Jul 25, 2018
Timoses
Jul 28, 2018
aliak
Jul 29, 2018
aliak
July 23, 2018
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
July 23, 2018
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
July 23, 2018
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 :(

July 23, 2018
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?
July 23, 2018
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
July 24, 2018
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?
July 24, 2018
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

July 25, 2018
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!
July 27, 2018
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
July 28, 2018
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.
« First   ‹ Prev
1 2