Thread overview
Normalize void
Jul 10, 2018
Yuxuan Shui
Jul 10, 2018
Yuxuan Shui
Jul 10, 2018
ag0aep6g
Jul 10, 2018
Yuxuan Shui
Jul 10, 2018
Mr.Bingo
Jul 10, 2018
Yuxuan Shui
July 10, 2018
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, 2018
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, 2018
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, 2018
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, 2018
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, 2018
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;
        }
    }