Jump to page: 1 2
Thread overview
Any way to automatically convert structs on return?
Aug 01
Emma
Aug 01
IchorDev
Aug 01
user1234
Aug 01
IchorDev
Aug 01
user1234
Aug 02
IchorDev
Aug 01
Dukc
Aug 01
Emma
Aug 02
Juraj
August 01

This code works:

struct None {}

struct Option(T) {
    bool hasSome;
    T value;

    this(None) {}

    this(T v) {
        hasSome = true;
        value = v;
    }
}

Option!int a = 123;    // automatically constructs an Option!int from a bare int
Option!int b = None(); // same as above but with None

but this doesn't:

Option!int something() {
  return None(); // Error: cannot implicitly convert expression `None()` of type `None` to `Option!int`
}

This kind of prevents ergonomic code like the above. Instead you have to use a function like Option!T None(T)() => Option!T() and then you have to repeat yourself with return None!int and etc... it's quite annoying :(

In C++ you may do this fairly easily, but of course there are various pitfalls because it's C++. But at least you can opt out with explicit most of the time.

Thanks in advance!

August 01
No, D does not support implicit construction.

However in saying that, I will continue to argue in support of sum types when they get added to the language to support implicit construction!

August 01

On Thursday, 1 August 2024 at 07:25:53 UTC, Emma wrote:

>

This code works:

struct None {}

struct Option(T) {
    bool hasSome;
    T value;

    this(None) {}

    this(T v) {
        hasSome = true;
        value = v;
    }
}

Option!int a = 123;    // automatically constructs an Option!int from a bare int
Option!int b = None(); // same as above but with None

but this doesn't:

Option!int something() {
  return None(); // Error: cannot implicitly convert expression `None()` of type `None` to `Option!int`
}

This kind of prevents ergonomic code like the above. Instead you have to use a function like Option!T None(T)() => Option!T() and then you have to repeat yourself with return None!int and etc... it's quite annoying :(

In C++ you may do this fairly easily, but of course there are various pitfalls because it's C++. But at least you can opt out with explicit most of the time.

Thanks in advance!

I’m pretty sure this is intentional to prevent ambiguity, but I can’t quite remember what the point of that is. You can always just write the constructor manually, but yes it’s a hassle. I wonder how open people would be to changing this restriction?

P.S. You might want to put value = void, otherwise it’ll always be default-constructed. Phobos also has Nullable if you want another implementation for reference.

August 01
Emma kirjoitti 1.8.2024 klo 10.25:
> This kind of prevents ergonomic code like the above. Instead you have to use a function like `Option!T None(T)() => Option!T()` and then you have to repeat yourself with `return None!int` and etc... it's quite annoying :(


While this isn't exactly less verbose, I mention it because at least you don't have to write the type twice:

```D
Option!int something() {
    return typeof(return)(None());
}
August 01

On Thursday, 1 August 2024 at 08:46:00 UTC, IchorDev wrote:

>

[...]
I’m pretty sure this is intentional to prevent ambiguity, but I can’t quite remember what the point of that is.

The problem would be that sorting the candidates of an overload set would be more complicated. Also in certain cases it would be less obvious to get which one is selected.

August 01

On Thursday, 1 August 2024 at 09:55:08 UTC, user1234 wrote:

>

The problem would be that sorting the candidates of an overload set would be more complicated. Also in certain cases it would be less obvious to get which one is selected.

Please elaborate about how this would interact with function overloads at all? The function has the same parameters, same return type, same attributes… what’s different exactly?

August 01

On Thursday, 1 August 2024 at 07:25:53 UTC, Emma wrote:

>

but this doesn't:

Option!int something() {
  return None(); // Error: cannot implicitly convert expression `None()` of type `None` to `Option!int`
}

For the program you've written, I'm happy it doesn't compile, because a lot of the time the compiler would be making buggy code compile - though I would love to have a way to do it explicitly. In this case you can change your definition of None to use alias this and it will compile:

struct None {
    alias convert this;

    Option!int convert() {
        return Option!int(None());
    }
}
August 01

On Thursday, 1 August 2024 at 10:59:03 UTC, IchorDev wrote:

>

On Thursday, 1 August 2024 at 09:55:08 UTC, user1234 wrote:

>

The problem would be that sorting the candidates of an overload set would be more complicated. Also in certain cases it would be less obvious to get which one is selected.

Please elaborate about how this would interact with function overloads at all?

That was a general criticism of implicit construction. The classic example is

struct S {int i;}
function f(S s);
function f(int i);

unittest { f(0); } // both match

unless the idea would rather be to allow implicit construction only in the context of initialization.

August 01

On Thursday, 1 August 2024 at 08:46:00 UTC, IchorDev wrote:

>

P.S. You might want to put value = void, otherwise it’ll always be default-constructed.

Doing = void can violate the assumptions of a destructor of T. Nullable uses a union to store T, so it can decide when to call the destructor.

August 01

Thanks everyone for the replies! I just thought it was weird that implicit construction is seemingly supported when directly initialising a struct but not in any other case. I guess it's because it's clearly less ambiguous.

On Thursday, 1 August 2024 at 13:07:09 UTC, Lance Bachmeier wrote:

>

For the program you've written, I'm happy it doesn't compile, because a lot of the time the compiler would be making buggy code compile - though I would love to have a way to do it explicitly. In this case you can change your definition of None to use alias this and it will compile:

The alias this doesn't work generically, though. Either way I agree that implicit construction, if it was present in D, should not be the default, instead being opt-in like say through an implicit this(...) or whatever instead of C++'s horrendous implicit by default behaviour.

« First   ‹ Prev
1 2