Thread overview
Normalize void
Jul 10
ag0aep6g
Jul 10
Mr.Bingo
July 10
Suppose I want to create a type to contain either a return value or an error, I could probably do something like this:

    struct Result(T, E) {
        bool is_err;
        union {
            T result;
            E error;
        }
    }

This will probably work fine, unless I don't need an error for some of the use cases (i.e. I want it to behave more like a Nullable). I can't just pass 'void' to 'E', because I can't define variable with type void. So I will have to:

    struct Result(T, E) {
        bool is_err;
        union {
            T result;
            static if (!is(E == void))
                E error;
        }
    }

I hope you can see what I mean here: 'void' is a special case I need to explicitly handle in templates. And special cases are bad.

What I want is for 'void' to behave like a normal type. This is not a crazy idea. 'void' can be considered as a unit type[0] in type theory. Basically, it is a type that can hold exactly 1 value (so you don't need any storage space to store it). And it exists in many programming languages.

D actually already partially have 'void' as a unit type. For example:

void a() { return a(); } // returning void in a void function

Why don't we make it consistent across the whole language?

Here is how 'void' would behave if we made it a unit type:

    void a; // fine
    pragma(msg, a.sizeof); // 0
    void b = a; // fine
    writeln(a); // prints something intelligent about void

    struct A {
        void placeholder; // fine
    }
    pragma(msg, A.sizeof); // 1, same as empty struct

[0]: https://en.wikipedia.org/wiki/Unit_type
July 10
On Tuesday, 10 July 2018 at 09:50:45 UTC, Yuxuan Shui wrote:
> Suppose I want to create a type to contain either a return value or an error, I could probably do something like this:
>
> [...]

Possible alternatives:

* struct Void {}. Takes 1 byte, not as ideal
* alias Void = AliasSeq!(). Doesn't work as template argument. i.e.
      SomeTemplate!Void; // actually become SomeTemplate!()
July 10
On Tuesday, 10 July 2018 at 09:50:45 UTC, Yuxuan Shui wrote:
> Suppose I want to create a type to contain either a return value or an error, I could probably do something like this:
>
> [...]

Breaking changes:

void[] x;
pragma(msg, x[0].sizeof); // now 0?
July 10
On 07/10/2018 11:56 AM, Yuxuan Shui wrote:
> Possible alternatives:
> 
> * struct Void {}. Takes 1 byte, not as ideal
> * alias Void = AliasSeq!(). Doesn't work as template argument. i.e.
>        SomeTemplate!Void; // actually become SomeTemplate!()

What about `void[0]`? It's a proper type. You can declare a field with it. Size is 0.
July 10
On Tuesday, 10 July 2018 at 11:37:25 UTC, ag0aep6g wrote:
> On 07/10/2018 11:56 AM, Yuxuan Shui wrote:
>> Possible alternatives:
>> 
>> * struct Void {}. Takes 1 byte, not as ideal
>> * alias Void = AliasSeq!(). Doesn't work as template argument. i.e.
>>        SomeTemplate!Void; // actually become SomeTemplate!()
>
> What about `void[0]`? It's a proper type. You can declare a field with it. Size is 0.

Nice!
July 10
On Tuesday, 10 July 2018 at 13:28:42 UTC, Yuxuan Shui wrote:
> On Tuesday, 10 July 2018 at 11:37:25 UTC, ag0aep6g wrote:
>> On 07/10/2018 11:56 AM, Yuxuan Shui wrote:
>>> Possible alternatives:
>>> 
>>> * struct Void {}. Takes 1 byte, not as ideal
>>> * alias Void = AliasSeq!(). Doesn't work as template argument. i.e.
>>>        SomeTemplate!Void; // actually become SomeTemplate!()
>>
>> What about `void[0]`? It's a proper type. You can declare a field with it. Size is 0.
>
> Nice!

How does that solve your original problem?


    struct Result(T, E) {
        bool is_err;
        union {
            T result;
            static if (!is(E == void))
                E error;
        }
    }

I thought you didn't want to have to specialize(meaning the static if)?

Doesn't seem like passing void[0] really solves that problem since you still might pass void.

I think the real solution is simply to never pass void! Then the static if is not needed



    struct Result(T = void[0], E = void[0]) {
        bool is_err;
        union {
            T result;
            E error;
        }
    }