Sum Types - first draft

Paul Backus snarwin at gmail.com
Tue Sep 10 19:56:28 UTC 2024


On Tuesday, 10 September 2024 at 19:13:33 UTC, Walter Bright 
wrote:
> On 9/10/2024 10:23 AM, Paul Backus wrote:
>>> I was approaching it from the other way around. Isn't a 
>>> non-nullable pointer a sumtype? Why have both non-nullable 
>>> types and sumtypes?
>> 
>> You have it exactly backwards. A _nullable_ pointer type is 
>> the sum of a non-nullable pointer type and typeof(null).
>
> "the sum of ..." doesn't make it a sumtype?
>
>> A non-nullable pointer type is a pointer type with its range 
>> of valid values restricted. You could think of it as a 
>> "difference type"--if you take T*, and _subtract_ typeof(null) 
>> from it (i.e., take the set difference [1] of their values), 
>> you get a non-nullable pointer type.
>> 
>> [1] 
>> https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement
>
> How is that different from a sumtype?

Let's walk through this very slowly, one step at a time.

One way to define a type is as a set of possible values.

For example:

     bool = {false, true}
     ubyte = {0, 1, 2, ..., 255}

Suppose we define a sum type of these two types:

     Example = bool + ubyte
     // Equivalent to:
     // alias Example = SumType!(bool, ubyte);

What is the set of possible values for the `Example` type? It's 
the set that contains all the elements of the `bool` set, and all 
the elements of the `ubyte` set--in other words, the set union. 
[1]

     Example = {false, true} ∪ {0, ..., 255} = {false, true, 0, 
..., 255}

Now, what are the possible values of a normal, nullable pointer 
type, like the ones we have in D today? Let's say we're on a 
32-bit architecture, just to make the numbers easier to write. In 
that case:

     uybte* = {null, 0x00000001, 0x00000002, ..., 0xFFFFFFFF}

Each 32-bit integer corresponds to a distinct pointer value, with 
0 corresponding to `null`.

Now, let's say we add non-nullable pointers to D, so that for 
every pointer type `T*`, there's a new type `nonnull(T*)` which 
is just like `T*`, except that it can't be null. What's the set 
of values for `nonnull(ubyte*)`?

     nonnull(ubyte*) = {0x00000001, 0x00000002, ..., 0xFFFFFFFF}

Naturally, it's the set of values for `ubyte*` with the value 
`null` removed. In set theory terms, we could write it as the 
difference between the `ubyte*` set, and the set that contains 
the single value `null`:

     nonnull(ubyte*) = ubyte* - {null}

As it turns out, there is actually a type in D that corresponds 
to the set `{null}`--it's called `typeof(null)`:

     typeof(null) = {null}

So, by substitution, we can write:

     nonnull(ubyte*) = ubyte* - typeof(null)

Or, in English: the type `nonnull(ubyte*)` is the _difference_ 
between the types `ubyte*` and `typeof(null)`.

Finally, what happens if we create a sum type of 
`nonnull(ubyte*)` and `typeof(null)`?

     Sum = nonnull(ubyte*) + typeof(null)
     Sum = {0x00000001, ..., 0xFFFFFFFF} ∪ {null}
     Sum = {null, 0x00000001, ..., 0xFFFFFFFF}

Wait a minute...we've seen that set before! It's the set for 
`ubyte*`!

So, once again, by substitution, we can write:

     Sum = ubyte*
     ubyte* = nonnull(ubyte*) + typeof(null)

Or, in English: the type `ubyte*` is the _sum_ of the types 
`nonnull(ubyte*)` and `typeof(null)`.

[1] https://en.wikipedia.org/wiki/Union_(set_theory)


More information about the dip.development mailing list