April 13, 2021
On 4/12/2021 11:14 AM, Timon Gehr wrote:
> non-compositional type system design is a game of whack-a-mole resulting in progressively more complexity while often maintaining a lack of soundness.

I could never have expressed the notion as clearly as that.
April 14, 2021
On Monday, 12 April 2021 at 18:14:30 UTC, Timon Gehr wrote:
> [snip]
>
> Unfortunately that's the nature of the chosen approach: non-compositional type system design is a game of whack-a-mole resulting in progressively more complexity while often maintaining a lack of soundness.

Sorry, when you say non-compositional type system, what precisely do you mean?
April 14, 2021
On Wednesday, 14 April 2021 at 11:43:22 UTC, jmh530 wrote:
> On Monday, 12 April 2021 at 18:14:30 UTC, Timon Gehr wrote:
>> [snip]
>>
>> Unfortunately that's the nature of the chosen approach: non-compositional type system design is a game of whack-a-mole resulting in progressively more complexity while often maintaining a lack of soundness.
>
> Sorry, when you say non-compositional type system, what precisely do you mean?

My guess is something like this:
https://dl.acm.org/doi/abs/10.1145/2036918.2036930
http://set.ee/publications/cats06.pdf
April 15, 2021
On 14.04.21 13:43, jmh530 wrote:
> On Monday, 12 April 2021 at 18:14:30 UTC, Timon Gehr wrote:
>> [snip]
>>
>> Unfortunately that's the nature of the chosen approach: non-compositional type system design is a game of whack-a-mole resulting in progressively more complexity while often maintaining a lack of soundness.
> 
> Sorry, when you say non-compositional type system, what precisely do you mean?
Types in a program are usually syntactically generated from some recursive grammar. For example, if you have types `T` and `S`, then `T[]` and `T delegate(S)` are also types.

Basically, the semantics of types is "compositional" when the meaning of the constituent types does not implicitly depend on the context in which they are plugged and the meaning of the context does not depend on the constituent types.

For example, above, the semantics of `T[]` and `T delegate(S)` are not fully compositional, because they have special cases for `T = void`.

Instances of such designs (here taken from C) tend to have ripple effects, slowly adding special cases in other corners of the language. For example, the compiler treats `return exp;` where exp is of type `void` like `exp; return;`. This is an improvement and I would not want to miss it, but it would not have been necessary nor needed any special documentation if `void` had not been special-cased in the first place. (In many languages, `void` is just treated like an ordinary type that has just one value, often identified with the empty tuple.)

The DIP is a bit like that, but on steroids, and it already proposes some of the ripple effects. All the while, it is not very powerful. For example, it does not even attempt to solve this case:

---
int apply(int delegate(int delegate(int)) f, int delegate(int) g){
    return f(g);
}
---

We want `apply` to have the same qualifiers as `f` and the argument of `f` to have the same qualifiers as `g`.

With proper attribute polymorphism, one can just state that in code:

---
int apply[qual a,qual b](int delegate(int delegate(int)a)b f, int delegate(int)a g)b{
    return f(g);
}
---

This solution is simple and more general, but most importantly, it does not redefine the meaning of existing types in non-compositional ways and the language does not paint itself into a corner by adopting such a solution. It does not break existing code nor prevent additional features to be added later.
April 14, 2021
On Wednesday, 14 April 2021 at 22:23:38 UTC, Timon Gehr wrote:
> [snip]
> With proper attribute polymorphism, one can just state that in code:
>
> ---
> int apply[qual a,qual b](int delegate(int delegate(int)a)b f, int delegate(int)a g)b{
>     return f(g);
> }
> ---
>
> This solution is simple and more general, but most importantly, it does not redefine the meaning of existing types in non-compositional ways and the language does not paint itself into a corner by adopting such a solution. It does not break existing code nor prevent additional features to be added later.

Thanks for the explanation!

I'm not sure I really grok all the ways it could be used, but it makes sense.

April 14, 2021
On Wednesday, 14 April 2021 at 22:23:38 UTC, Timon Gehr wrote:
> For example, above, the semantics of `T[]` and `T delegate(S)` are not fully compositional, because they have special cases for `T = void`.
>

How is void delegate(S) a spcial case?

Great post, BTW.

April 15, 2021

On Wednesday, 14 April 2021 at 23:51:33 UTC, deadalnix wrote:

>

On Wednesday, 14 April 2021 at 22:23:38 UTC, Timon Gehr wrote:

>

For example, above, the semantics of T[] and T delegate(S) are not fully compositional, because they have special cases for T = void.

How is void delegate(S) a spcial case?

Great post, BTW.

You can't use void as a type on its own, but you can use it in certain specific contexts, like void delegate(S) and void[]. Those specific contexts are special cases: they're not derived from any general rule, but have to be spelled out explicitly in the language spec. (For example, https://dlang.org/spec/arrays.html#void_arrays)

April 15, 2021

On Thursday, 15 April 2021 at 01:15:34 UTC, Paul Backus wrote:

>

On Wednesday, 14 April 2021 at 23:51:33 UTC, deadalnix wrote:

>

On Wednesday, 14 April 2021 at 22:23:38 UTC, Timon Gehr wrote:

>

For example, above, the semantics of T[] and T delegate(S) are not fully compositional, because they have special cases for T = void.

How is void delegate(S) a spcial case?

Great post, BTW.

You can't use void as a type on its own, but you can use it in certain specific contexts, like void delegate(S) and void[]. Those specific contexts are special cases: they're not derived from any general rule, but have to be spelled out explicitly in the language spec. (For example, https://dlang.org/spec/arrays.html#void_arrays)

idk, it seems to be that not being able to declare a void variable is the special case. There are expression in D of type void, and they compose like the rest of it.

April 15, 2021

On Thursday, 15 April 2021 at 13:33:37 UTC, deadalnix wrote:

>

On Thursday, 15 April 2021 at 01:15:34 UTC, Paul Backus wrote:

>

On Wednesday, 14 April 2021 at 23:51:33 UTC, deadalnix wrote:

>

On Wednesday, 14 April 2021 at 22:23:38 UTC, Timon Gehr wrote:

>

For example, above, the semantics of T[] and T delegate(S) are not fully compositional, because they have special cases for T = void.

How is void delegate(S) a spcial case?

Great post, BTW.

You can't use void as a type on its own, but you can use it in certain specific contexts, like void delegate(S) and void[]. Those specific contexts are special cases: they're not derived from any general rule, but have to be spelled out explicitly in the language spec. (For example, https://dlang.org/spec/arrays.html#void_arrays)

idk, it seems to be that not being able to declare a void variable is the special case. There are expression in D of type void, and they compose like the rest of it.

You are right, but that’s just another special case for void (that should just be fixed imo, I think Dennis is working on a DIP for that). void arrays are still a special case because they are the super type of all arrays. This is completely contrary to how array subtyping works for basically all other types. But this is starting to get off topic.

April 15, 2021
On 15.04.21 01:51, deadalnix wrote:
> On Wednesday, 14 April 2021 at 22:23:38 UTC, Timon Gehr wrote:
>> For example, above, the semantics of `T[]` and `T delegate(S)` are not fully compositional, because they have special cases for `T = void`.
>>
> 
> How is void delegate(S) a spcial case?
> ...

`void` has no semantics as a type of a value, so any place where it is valid where ordinarily such a type would be requested is a special case.

> Great post, BTW.
> 

Thanks!