Jump to page: 1 2 3
Thread overview
Discussion Thread: DIP 1041--Attributes for Higher-Order Functions--Community Review Round 1
Apr 12, 2021
Mike Parker
Apr 12, 2021
Mike Parker
Apr 12, 2021
Timon Gehr
Apr 12, 2021
Q. Schroll
Apr 12, 2021
ag0aep6g
Apr 13, 2021
Timon Gehr
Apr 25, 2021
Q. Schroll
Apr 12, 2021
Imperatorn
Apr 12, 2021
Timon Gehr
Apr 14, 2021
Walter Bright
Apr 14, 2021
jmh530
Apr 14, 2021
Imperatorn
Apr 14, 2021
Timon Gehr
Apr 14, 2021
jmh530
Apr 14, 2021
deadalnix
Apr 15, 2021
Paul Backus
Apr 15, 2021
deadalnix
Apr 15, 2021
Johannes Loher
Apr 15, 2021
Timon Gehr
Apr 15, 2021
Timon Gehr
Apr 12, 2021
12345swordy
Apr 13, 2021
Kagamin
April 12, 2021

Discussion Thread

This is the discussion thread for the first round of Community Review of DIP 1041, "Attributes for Higher-Order Functions":

https://github.com/dlang/DIPs/blob/11fcb0f79ce7ec209fb2a302d1371722d0c8ad82/DIPs/DIP1041.md

The review period will end at 11:59 PM ET on April 26, or when I make a post declaring it complete. Discussion in this thread may continue beyond that point.

Here in the discussion thread, you are free to discuss anything and everything related to the DIP. Express your support or opposition, debate alternatives, argue the merits, etc.

However, if you have any specific feedback on how to improve the proposal itself, then please post it in the Feedback Thread. The Feedback Thread will be the source for the review summary that I will write at the end of this review round. I will post a link to that thread immediately following this post. Just be sure to read and understand the Reviewer Guidelines before posting there:

https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md

And my blog post on the difference between the Discussion and Feedback threads:

https://dlang.org/blog/2020/01/26/dip-reviews-discussion-vs-feedback/

Please stay on topic here. I will delete posts that are completely off-topic.

April 12, 2021

On Monday, 12 April 2021 at 09:36:29 UTC, Mike Parker wrote:

>

However, if you have any specific feedback on how to improve the proposal itself, then please post it in the Feedback Thread. The Feedback Thread will be the source for the review summary that I will write at the end of this review round. I will post a link to that thread immediately following this post.

The Feedback Thread is located here:

https://forum.dlang.org/post/zureulajwutgsynerwzc@forum.dlang.org

April 12, 2021
On 12.04.21 16:44, Q. Schroll wrote:
>
> On Monday, 12 April 2021 at 11:05:14 UTC, Timon Gehr wrote:
>> On 12.04.21 11:38, Mike Parker wrote:
>>> https://github.com/dlang/DIPs/blob/11fcb0f79ce7ec209fb2a302d1371722d0c8ad82/DIPs/DIP1041.md ...
>>
>> Unfortunately, it is not written too well: The reader gets flooded with details way before being told what the problem actually is or how the proposal addresses it.
>
> Doesn't the Abstract explain what the problem is and give a general idea how it is addressed?
> ...

It does not. It's generic fluff. It's only marginally more explicit than: "there are problems and to address them we should change the language".

>> As far as I can tell, this is trying to introduce attribute polymorphism without actually adding polymorphism, much like `inout` attempted and ultimately failed to do. I am very skeptical. It's taking a simple problem with a simple solution and addressing it using an overengineered non-orthogonal mess in the hopes of not having to add additional syntax.
>
> You're mistaken. You can take a look at the Alternatives for seemingly simple solutions. There ain't any.

I know there are, and I literally state how to do it in the quoted excerpt.

> Because D isn't an immutable-all-the-way-down language like e.g. Haskell,

Haskell has plenty of support for mutable data.

> none of the easy solutions are sound. You always have the problem of assigning the parameter in the functional unless it's `const` or another flavor of non-mutable.

Assignments are not the problem, it's the inconsistent interpretation of types using incompatible, special-cased meanings.

> If you don't go the `const` route, you have to deal with assignments to the parameter before it's called. You have to disallow assignments that, looking at the types, are a 1-to-1 assignment. IMO, going via `const` is far more intuitive.
> ...

It's a bad, non-orthogonal solution building on a compiler bug.

> In fact, "not having to add additional syntax" was never the motivation for the proposal. Not having to introduce attributes _specific_ to higher-order function was.
> ...

It adds higher-order specific rules to existing attributes without a good reason, which is a lot worse.

>> To add insult to injury, the first example that's shown in the DIP as motivation abuses an existing type system hole.
>
> I disagree that it is a hole in the type system.

You are wrong, and I am not sure how to make that point to you. (When I tried last time, you just claimed that some other well-documented intentionally-designed feature, like attribute transitivity, is actually a bug.)

> When having `qual₁(R delegate(Ps) qual₂)` where `qual₁` and `qual₂` are type qualifiers (`const`, `immutable`, etc.) it is practically most useful if `qual₁` only applies to the function pointer and (the outermost layer of) the context pointer while `qual₂` refers to the property of the context itself.

That allows building a gadget to completely bypass transitivity of qualifiers, including `immutable` and `shared`.

It's completely unsound, e.g., it allows creating race conditions in `@safe` code.

You can't say qualifiers are transitive except in this one case, that translates to them not being transitive at all. A lot of D's type system design is built on qualifiers being transitive.

> Since the language gives no non-UB way to assign the function pointer and the context pointer separately, it is not unsound.
> [Here](https://forum.dlang.org/post/gauloixsonnnlswhbiqe@forum.dlang.org) is the space for discussing this issue. Unfortunately, it's mostly us two that care.
>

The obfuscated DIP is unfortunately not helping with that.

In any case, this is now the place to discuss this issue as you have chosen to use this bug as a basis for evolving the language.

>> `toString` is `const`, `sink` is `const`, the only reference to result accessible to `toString` is in the context of `sink`, but somehow `result` is mutated anyway.
>
> See the paragraph above.
>
>> Unsoundness should be fixed, not embraced!
>
> Yes, I guess no one disagrees on that one, but on the question of it being an instance of it.
>
>> Finally, there's this concern: The DIP assumes that the only reasonable way to manipulate delegates in higher-order functions involves calling them, but this is not accurate.
>
> It assumes that the the most common use-case of non-mutable delegate parameters is only calling them. Returning them is another, but a rarer one. The DIP details that in this case, the author of the `compose` function needs to remember not to make the parameters mutable.
> ...

I guess you mean the other way around.

>> ```D
>> auto compose(A,B,C)(C delegate(B) f, B delegate(A) g)pure{
>>     return a=>f(g(a));
>> }
>> ```
>>
>> With the proposed changes, composing impure functions suddenly becomes an impure operation as soon as you abstract it into a higher-order function. This is pure nonsense. If you have a `pure` expression and abstract it into a `pure` function, it should not become less `pure` in the process!
>
> You did it correctly in the sense of the DIP. `compose` takes `f` and `g` as mutable. None of the proposed changes apply to mutable delegate parameters.

Fair, but that's a technicality tangential to my point, and as you may have been able to tell, I have certain reservations about reusing `const` in this fashion.

> By the changes proposed by this DIP, `compose` is `pure`. However, all delegates you pass to it lose information of attributes because you _could_ assign `f` or `g` in `compose`, no problem.
> ...

But that's a terrible reason to not be able to annotate them `const`. `const` means "this won't change", it does not mean "if you compose this, it won't be recognized as `pure`" and there is no clear way to get from one to the other. It's a textbook example of a messy non-orthogonal design that does not make any sense upon closer inspection.

> But as you don't intend to mutate `f` or `g` in it, you could get the idea of making them `const` like this:

Yes, let's assume that was my intention.

> ```D
> C delegate(A) compose(A, B, C)(const C delegate(B) f, const B delegate(A) g) pure
> {
>     return a => f(g(a));
> }
> ```
> Then, by the proposed changes, only `pure` arguments lead to a `pure` call expression.

Which was my point. This is indefensible.

> However, `compose` is a good example why this is not an issue: It is already a template. Why not go the full route and make the `delegate` part of the template type arguments like this:
> ```D
> auto compose(F : C delegate(B), G : B delegate(A), A, B, C)(F f, G g) pure
> {
>     return delegate C(A arg) => f(g(arg));
> }
> ```

The fact that there is some ugly workaround for my illustrative example that also defeats the point of your DIP does not eliminate the problem with the DIP.

> Unfortunately (see [here](https://issues.dlang.org/show_bug.cgi?id=21823)), when calling `compose` with a `const` declared delegate value, D's IFTI infers `const` for the parameter type although the parameter is copied. I didn't realize that when writing the DIP. This is a problem, but it can be fixed (probably should).

(Your reinterpretation of what delegate qualifiers mean would need a DIP in its own right and it would hopefully be rejected.)

April 12, 2021

On Monday, 12 April 2021 at 09:36:29 UTC, Mike Parker wrote:

>

Discussion Thread

This is the discussion thread for the first round of Community Review of DIP 1041, "Attributes for Higher-Order Functions":

[...]

I think the DIP has a noble goal, but is too complex. Try to keep it minimal if possible

April 12, 2021
On 12.04.21 20:09, Imperatorn wrote:
> On Monday, 12 April 2021 at 09:36:29 UTC, Mike Parker wrote:
>> ## Discussion Thread
>>
>> This is the discussion thread for the first round of Community Review of DIP 1041, "Attributes for Higher-Order Functions":
>>
>> [...]
> 
> I think the DIP has a noble goal, but is too complex. Try to keep it minimal if possible

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.
April 12, 2021

On Monday, 12 April 2021 at 09:36:29 UTC, Mike Parker wrote:

>

Discussion Thread

This is the discussion thread for the first round of Community Review of DIP 1041, "Attributes for Higher-Order Functions":

https://github.com/dlang/DIPs/blob/11fcb0f79ce7ec209fb2a302d1371722d0c8ad82/DIPs/DIP1041.md

The review period will end at 11:59 PM ET on April 26, or when I make a post declaring it complete. Discussion in this thread may continue beyond that point.

Here in the discussion thread, you are free to discuss anything and everything related to the DIP. Express your support or opposition, debate alternatives, argue the merits, etc.

However, if you have any specific feedback on how to improve the proposal itself, then please post it in the Feedback Thread. The Feedback Thread will be the source for the review summary that I will write at the end of this review round. I will post a link to that thread immediately following this post. Just be sure to read and understand the Reviewer Guidelines before posting there:

https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md

And my blog post on the difference between the Discussion and Feedback threads:

https://dlang.org/blog/2020/01/26/dip-reviews-discussion-vs-feedback/

Please stay on topic here. I will delete posts that are completely off-topic.

From speed reading this dip, any benefits that is to be gain by this dip is overweight by the sheer complexity that is introduced by this dip.

-Alex

April 12, 2021

On Monday, 12 April 2021 at 17:21:47 UTC, Timon Gehr wrote:

>

On 12.04.21 16:44, Q. Schroll wrote:

>

On Monday, 12 April 2021 at 11:05:14 UTC, Timon Gehr wrote:

>

Unfortunately, it is not written too well: The reader gets flooded with details way before being told what the problem actually is or how the proposal addresses it.

Doesn't the Abstract explain what the problem is and give a general idea how it is addressed?

It does not. It's generic fluff. It's only marginally more explicit than: "there are problems and to address them we should change the language".

I had a more detailed Abstract in previous drafts, but if you think I watered it down too much, I can add more details.

> > >

As far as I can tell, this is trying to introduce attribute polymorphism without actually adding polymorphism, much like inout attempted and ultimately failed to do. I am very skeptical. It's taking a simple problem with a simple solution and addressing it using an overengineered non-orthogonal mess in the hopes of not having to add additional syntax.

You're mistaken. You can take a look at the Alternatives for seemingly simple solutions. There ain't any.

I know there are, and I literally state how to do it in the quoted excerpt.

If by "quoted excerpt" you mean "As far as I can tell, this", I read it, but to be honest, I didn't really understand what attribute polymorphism really means. Googling "polymorphism" the closet I come to would be that a @safe delegate can be used in place of a @system delegate. This is already the case, I can't see how anything would "introduce" it.

> >

You always have the problem of assigning the parameter in the functional unless it's const or another flavor of non-mutable.

Assignments are not the problem, it's the inconsistent interpretation of types using incompatible, special-cased meanings.

Maybe I'm not creative enough for a proper solution, but I should be, since the problem is "easy".

> >

If you don't go the const route, you have to deal with assignments to the parameter before it's called. You have to disallow assignments that, looking at the types, are a 1-to-1 assignment. IMO, going via const is far more intuitive.

It's a bad, non-orthogonal solution building on a compiler bug.

>

In fact, "not having to add additional syntax" was never the motivation for the proposal. Not having to introduce attributes specific to higher-order function was.

It adds higher-order specific rules to existing attributes without a good reason, which is a lot worse.

I guess removing higher-order functions as a road-bump when it comes to attributes is a good reason. It's adding higher-order specific rules vs. adding another higher-order specific something.

> > >

To add insult to injury, the first example that's shown in the DIP as motivation abuses an existing type system hole.

I disagree that it is a hole in the type system.

You are wrong, and I am not sure how to make that point to you. (When I tried last time, you just claimed that some other well-documented intentionally-designed feature, like attribute transitivity, is actually a bug.)

>

When having qual₁(R delegate(Ps) qual₂) where qual₁ and qual₂ are type qualifiers (const, immutable, etc.) it is practically most useful if qual₁ only applies to the function pointer and (the outermost layer of) the context pointer while qual₂ refers to the property of the context itself.

That allows building a gadget to completely bypass transitivity of qualifiers, including immutable and shared.

I had a look at issue 1983 again where (I guess) the source of disagreement is how delegates should be viewed theoretically. If I understand you correctly, you say delegates cannot possibly be defined differently than having their contexts be literally part of them. I tried to explore definitions in which the context is associated with but not literally part of the delegate.

My goal was to find a theoretic foundation that is practically useful and doesn't defy expectations. For if a closure mutates a captured variable, one can't assign that closure to a const variable, notably, you cannot bind it to a functional's const parameter, I guess does defy expectations greatly.

Trying to draw a comparison with it, I found out today that slice's capacity is pure and also that it's a bug admitted in object.d ("This is a lie. [It] is neither nothrow nor pure, but this lie is necessary for now to prevent breaking code.")

>

It's completely unsound, e.g., it allows creating race conditions in @safe code.

Maybe I'm just too uncreative or too dumb to come up with one myself. I once ran into something like that trying out std.parallelism.parallel and how much it could gain me. It's years ago and I cannot remember a lot. I figured it wasn't applicable in my case. The

I'd really appreciate an example from your side.

>

You can't say qualifiers are transitive except in this one case, that translates to them not being transitive at all. A lot of D's type system design is built on qualifiers being transitive.

>

Since the language gives no non-UB way to assign the function pointer and the context pointer separately, it is not unsound.
Here is the space for discussing this issue. Unfortunately, it's mostly us two that care.

The obfuscated DIP is unfortunately not helping with that.

In any case, this is now the place to discuss this issue as you have chosen to use this bug as a basis for evolving the language.

> >

toString is const, sink is const, the only reference to result accessible to toString is in the context of sink, but somehow result is mutated anyway.

See the paragraph above.

>

Unsoundness should be fixed, not embraced!

Yes, I guess no one disagrees on that one, but on the question of it being an instance of it.

>

Finally, there's this concern: The DIP assumes that the only reasonable way to manipulate delegates in higher-order functions involves calling them, but this is not accurate.

It assumes that the the most common use-case of non-mutable delegate parameters is only calling them. Returning them is another, but a rarer one. The DIP details that in this case, the author of the compose function needs to remember not to make the parameters mutable.

I guess you mean the other way around.

Yes. I meant to say: "needs to remember to make the parameters mutable.".

> > >
auto compose(A,B,C)(C delegate(B) f, B delegate(A) g)pure{
    return a=>f(g(a));
}

With the proposed changes, composing impure functions suddenly becomes an impure operation as soon as you abstract it into a higher-order function. This is pure nonsense. If you have a pure expression and abstract it into a pure function, it should not become less pure in the process!

You did it correctly in the sense of the DIP. compose takes f and g as mutable. None of the proposed changes apply to mutable delegate parameters.

Fair, but that's a technicality tangential to my point, and as you may have been able to tell, I have certain reservations about reusing const in this fashion.

>

By the changes proposed by this DIP, compose is pure. However, all delegates you pass to it lose information of attributes because you could assign f or g in compose, no problem.

But that's a terrible reason to not be able to annotate them const. const means "this won't change", it does not mean "if you compose this, it won't be recognized as pure" and there is no clear way to get from one to the other. It's a textbook example of a messy non-orthogonal design that does not make any sense upon closer inspection.

Maybe use in (i.e. const scope) then? It clearly signifies: This is to read information from, not to assign to it, assign it to a global, not even to return it in any fashion.

> >

But as you don't intend to mutate f or g in it, you could get the idea of making them const like this:

Yes, let's assume that was my intention.

>
C delegate(A) compose(A, B, C)(const C delegate(B) f, const B delegate(A) g) pure
{
    return a => f(g(a));
}

Then, by the proposed changes, only pure arguments lead to a pure call expression.

Which was my point. This is indefensible.

It suffices to write this and one @safe unit test: The compile error will tell you there's a problem. I can add to the Error Messages section that in this case, the error message should hint that the const might be used improperly.

> >

However, compose is a good example why this is not an issue: It is already a template. Why not go the full route and make the delegate part of the template type arguments like this:

auto compose(F : C delegate(B), G : B delegate(A), A, B, C)(F f, G g) pure
{
    return delegate C(A arg) => f(g(arg));
}

The fact that there is some ugly workaround for my illustrative example that also defeats the point of your DIP does not eliminate the problem with the DIP.

This isn't an ugly workaround, but merely an attempt to stick to the example. Simply omitting the specialization syntax isn't possible. return a => f(g(a)); doesn't compile, you need the (A a) part and for that, you need A. You can get it alternatively with Parameters!f; but auto compose(F, G)(F f, G g) with return a => f(g(a)); doesn't work.

>

Your reinterpretation of what delegate qualifiers mean would need a DIP in its own right and it would hopefully be rejected.

I'm not sure it's a *re-*interpretation. As factually the compiler defines the language at places, you're probably right about the DIP part.

April 12, 2021

On Monday, 12 April 2021 at 21:59:50 UTC, Q. Schroll wrote:

>

On Monday, 12 April 2021 at 17:21:47 UTC, Timon Gehr wrote:
[...]

>

Your reinterpretation of what delegate qualifiers mean would need a DIP in its own right and it would hopefully be rejected.

I'm not sure it's a *re-*interpretation. As factually the compiler defines the language at places, you're probably right about the DIP part.

I don't think you can cite DMD on the matter. It contradicts itself when it comes to qualified delegates.

You could point at the following code as evidence that DMD allows a const delegate with a mutable context:

struct S
{
    int field;
    void method() { field = 42; }
}
void main()
{
    S s;
    const void delegate() d = &s.method;
    d();
}

But add one line, and DMD will say the opposite:

alias NotEvenUsed = const void delegate() const; /* the only change; rest of the code is identical */
struct S
{
    int field;
    void method() { field = 42; }
}
void main()
{
    S s;
    const void delegate() d = &s.method; /* this is now an error */
    d();
}

https://issues.dlang.org/show_bug.cgi?id=16058

This is a case where we can't just say "let's write down in the spec what DMD is doing already", because what DMD is doing makes no sense.

April 13, 2021
On 4/12/21 11:59 PM, Q. Schroll wrote:
> On Monday, 12 April 2021 at 17:21:47 UTC, Timon Gehr wrote:
>> On 12.04.21 16:44, Q. Schroll wrote:
>>>
>>> On Monday, 12 April 2021 at 11:05:14 UTC, Timon Gehr wrote:
>>>> Unfortunately, it is not written too well: The reader gets flooded with details way before being told what the problem actually is or how the proposal addresses it.
>>>
>>>
>>> Doesn't the Abstract explain what the problem is and give a general idea how it is addressed?
>>
>> It does not. It's generic fluff. It's only marginally more explicit than: "there are problems and to address them we should change the language".
> 
> I had a more detailed Abstract in previous drafts, but if you think I watered it down too much, I can add more details.
> 
>>>> As far as I can tell, this is trying to introduce attribute polymorphism without actually adding polymorphism, much like `inout` attempted and ultimately failed to do. I am very skeptical. It's taking a simple problem with a simple solution and addressing it using an overengineered non-orthogonal mess in the hopes of not having to add additional syntax.
>>>
>>> You're mistaken. You can take a look at the Alternatives for seemingly simple solutions. There ain't any.
>>
>> I know there are, and I literally state how to do it in the quoted excerpt.
> 
> If by "quoted excerpt" you mean "As far as I can tell, this", I read it, but to be honest, I didn't really understand what attribute polymorphism really means. Googling "polymorphism" the closet I come to would be that a `@safe` delegate can be used in place of a `@system` delegate. This is already the case, I can't see how anything would "introduce" it.
> ...

That's subtyping, not polymorphism. Polymorphism is when a term depends on a type:
https://en.wikipedia.org/wiki/Lambda_cube
https://en.wikipedia.org/wiki/Parametric_polymorphism

In this case, a term would depend on an attribute.

>> > You always have the problem of assigning the parameter in the > 
>> functional unless it's `const` or another flavor of > non-mutable.
>>
>> Assignments are not the problem, it's the inconsistent interpretation of types using incompatible, special-cased meanings.
> 
> Maybe I'm not creative enough for a proper solution, but I should be, since the problem is "easy".
> ...

I am not saying it is easy. I am saying humanity has a rich cultural history and this happens to be a solved problem.

>>> If you don't go the `const` route, you have to deal with assignments to the parameter before it's called. You have to disallow assignments that, looking at the types, are a 1-to-1 assignment. IMO, going via `const` is far more intuitive.
>>
>> It's a bad, non-orthogonal solution building on a compiler bug.
>>
>>> In fact, "not having to add additional syntax" was never the motivation for the proposal. Not having to introduce attributes _specific_ to higher-order function was.
>>
>> It adds higher-order specific rules to existing attributes without a good reason, which is a lot worse.
> 
> I guess removing higher-order functions as a road-bump when it comes to attributes is a good reason. It's adding higher-order specific rules vs. adding another higher-order specific something.
> ...

It does not have to be higher-order specific at all. Might as well fix `inout` at the same time.

>>>> To add insult to injury, the first example that's shown in the DIP as motivation abuses an existing type system hole.
>>>
>>> I disagree that it is a hole in the type system.
>>
>> You are wrong, and I am not sure how to make that point to you. (When I tried last time, you just claimed that some other well-documented intentionally-designed feature, like attribute transitivity, is actually a bug.)
>>
>>> When having `qual₁(R delegate(Ps) qual₂)` where `qual₁` and `qual₂` are type qualifiers (`const`, `immutable`, etc.) it is practically most useful if `qual₁` only applies to the function pointer and (the outermost layer of) the context pointer while `qual₂` refers to the property of the context itself.
>>
>> That allows building a gadget to completely bypass transitivity of qualifiers, including `immutable` and `shared`.
> 
> I had a look at [issue 1983](https://issues.dlang.org/show_bug.cgi?id=1983) again where (I guess) the source of disagreement is how delegates should be viewed theoretically. If I understand you correctly, you say delegates *cannot possibly* be defined differently than having their contexts be literally part of them. I tried to explore definitions in which the context is *associated with* but not *literally part of* the delegate.
> ...

I get that, but it is impossible because you can use delegate contexts as arbitrary storage:

int x;
auto dg=(int* update){
    if(update) x=*update;
    return x;
};

If you can have "associated" delegate contexts, you can have "associated" struct fields. But we don't have those. Assuming the concept has merit, it would still be bad design to abitrarily tie it to delegate contexts.


> My goal was to find a theoretic foundation that is practically useful and doesn't defy expectations. For if a closure mutates a captured variable, one can't assign that closure to a `const` variable, notably, you cannot bind it to a functional's `const` parameter, I guess does defy expectations greatly.
> ...

You can store it in a `const` variable, but you can't call it, much like you can't call a mutable method on a `const` object.


> Trying to draw a comparison with it, I found out today that slice's `capacity` is `pure` and also that it's a bug admitted in `object.d` ("This is a lie. [It] is neither `nothrow` nor `pure`, but this lie is necessary for now to prevent breaking code.")
> 
>> It's completely unsound, e.g., it allows creating race conditions in `@safe` code.
> 
> Maybe I'm just too uncreative or too dumb to come up with one myself. I once ran into something like that trying out `std.parallelism.parallel` and how much it could gain me.

std.parallelism.parallel cannot be annotated @safe or @trusted.

> It's years ago and I cannot remember a lot. I figured it wasn't applicable in my case. The
>
> I'd really appreciate an example from your side.
> ...

E.g., this:

import std.concurrency;
void main(){
    int x;
    // this conversion should not go through
    shared(int delegate(int*)) dg=(int* update){
        if(update) x=*update;
        return x;
    };
    spawn((typeof(dg) dg){
        int y=3;
        dg(&y); // this should not be callable
    },dg);
    import std.stdio;
    writeln(x);
}

>>> By the changes proposed by this DIP, `compose` is `pure`. However, all delegates you pass to it lose information of attributes because you _could_ assign `f` or `g` in `compose`, no problem.
>>
>> But that's a terrible reason to not be able to annotate them `const`. `const` means "this won't change", it does not mean "if you compose this, it won't be recognized as `pure`" and there is no clear way to get from one to the other. It's a textbook example of a messy non-orthogonal design that does not make any sense upon closer inspection.
> 
> Maybe use `in` (i.e. `const scope`) then? It clearly signifies: This is to read information from, not to assign to it, assign it to a global, not even to return it in any fashion.
> ...

It's not what you need. Reassigning is fine, it just has to be something with compatible attributes.

>>> But as you don't intend to mutate `f` or `g` in it, you could get the idea of making them `const` like this:
>>
>> Yes, let's assume that was my intention.
>>
>>> ```D
>>> C delegate(A) compose(A, B, C)(const C delegate(B) f, const B delegate(A) g) pure
>>> {
>>>     return a => f(g(a));
>>> }
>>> ```
>>> Then, by the proposed changes, only `pure` arguments lead to a `pure` call expression.
>>
>> Which was my point. This is indefensible.
> 
> It suffices to write this and one `@safe` unit test: The compile error will tell you there's a problem. I can add to the Error Messages section that in this case, the error message should hint that the `const` might be used improperly.
> ...

The error message would have to say it was designed improperly.

>>> However, `compose` is a good example why this is not an issue: It is already a template. Why not go the full route and make the `delegate` part of the template type arguments like  this:
>>> ```D
>>> auto compose(F : C delegate(B), G : B delegate(A), A, B, C)(F f, G g) pure
>>> {
>>>     return delegate C(A arg) => f(g(arg));
>>> }
>>> ```
>>
>> The fact that there is some ugly workaround for my illustrative example that also defeats the point of your DIP does not eliminate the problem with the DIP.
> 
> This isn't an ugly workaround,

ugly, check, workaround, check.

> but merely an attempt to stick to the example. Simply omitting the specialization syntax isn't possible. `return a => f(g(a));` doesn't compile, you need the `(A a)` part and for that, you need `A`. You can get it alternatively with `Parameters!f`; but `auto compose(F, G)(F f, G g)` with `return a => f(g(a));` doesn't work.
> ...

None of this matters. You "solved" the problem by removing the need for attribute polymorphism using automated code duplication.

>> Your reinterpretation of what delegate qualifiers mean would need a DIP in its own right and it would hopefully be rejected.
> 
> I'm not sure it's a *re-*interpretation.

It defies attribute transitivity, which is a stated design goal.

> As factually the compiler defines the language at places, you're probably right about the DIP part.

Unfortunately, the compiler has bugs. One can't take its behavior as holy gospel that just needs to be interpreted correctly.
April 13, 2021

As I understand, the author proposes to implicitly cast gc delegates to nogc delegates, which sounds scary.

« First   ‹ Prev
1 2 3