Feedback Thread: DIP 1033--Implicit Conversion of Expressions to Delegates--Final Review

MD-39 39.m.delnis at gmail.com
Tue Nov 24 23:45:24 UTC 2020


On Sunday, 22 November 2020 at 08:46:16 UTC, Walter Bright wrote:
> On 11/20/2020 4:40 PM, MD-39 wrote:
>> There's a few problems and some things I don't really agree 
>> with. I'll start with the most obvious problem regarding the 
>> example with absolutePath.
>> 
>>> pure @safe string absolutePath(string path, string delegate() 
>>> base = getcwd());
>> 
>> This becomes (per the DIP):
>> 
>> pure @safe string absolutePath(string path, string delegate() 
>> base = () @trusted { return getcwd() });
>> 
>> Which would still give an error, though the delegate 
>> encapsulating getcwd() is auto inferred, the actually 
>> parameter isn't auto inferred according to existing rules.
>> 
>> The parameter `base` is both impure and @unsafe. Unless there 
>> is some auto inference happening here this would fail to 
>> compile. It wouldn't be used the same way, the implementation 
>> would have to change to assume `base` is pure and @safe.
>> 
>> If there is auto inference of parameter types, the DIP needs 
>> to outline the details for this. Again, it would still be 
>> impure even if it auto inferred to be @trusted from `getcwd`. 
>> So again, the implementation would have to change to assume 
>> `base` is pure.
>> 
>> In either case, the code is broken and/or is giving the 
>> illusion that the problem at hand is simpler than it actually 
>> is by masking implementation details.
>
> It won't compile, and that seems correct to me.

Yah I can clarify that misunderstanding for you.

This is actually the error referred to below. The DIP gives this 
signature as the equivalent to "pure @safe string 
absolutePath(string path, lazy string base = getcwd());". But as 
you stated the equivalent is understandably not going to compile. 
An actual equivalent to the signature absolutePath() should be 
given that is intended to actually compile and work.

>
>> The DIP also doesn't go over one of the common discussions 
>> regarding lazy that is brought up from time to time; in 
>> regards to readability. This would be a good case to implement 
>> it. For example in C# at the call site the keyword `ref` has 
>> to be added when passing an argument to a ref parameter. 
>> Similarly you would just use a lambda `() =>` in C#, there is 
>> no special case to implicitly convert to a delegate.
>
> The C# style makes refactoring code harder. Instead of 
> modifying the callee, you have to additionally modify all of 
> the callers.

I can clarify this as well.

Making it harder to refactor code also decreases the number of 
potential bugs. Consider the following code:

     int i = 0;
     foo( ++i );
     assert( i == 1 ); // change foo() to use delegate

If you were to change foo() to take a delegate you would now be 
changing the behavior everywhere foo is used. By requiring `() 
=>` you would force the user to add the checks and make it 
visibly more readable. This was a common complaint of `lazy`, 
that you can't tell whether code at the call site is being 
executed there or not. Now sure the example is practical, but 
unless the parameter is a constant it will change what the code 
does.

Also consider implicit conversion with D's structs. The argument 
could be made that it makes refactoring easier if implicit 
conversions were allowed. Implicit conversions are only allowed 
with assignments for this reason. Implicit conversions have 
negative unpredictable side effects; as is the case with feature.


>>> int delegate() dg = () { return 3; };
>>> int delegate() dg = () => 3;
>>> int delegate() dg = { return 3; };
>>> int delegate() dg = 3;
>> 
>> When you have 4 ways to express the exact same thing, there is 
>> an obvious design problem. You shouldn't need 4 different ways 
>> to express the same thing.
>
> This comes from the desire to dispense with unnecessary syntax.

That is an admirable goal, but what it ends up creating is 
complexity.

>> The existing Lambda syntax already has a lot of confusion, it 
>> isn't uncommon for individuals to mistakenly do the following:
>> 
>>      auto dg = x => { return x * x; };
>> 
>> Which doesn't do what it appears to do.
>> 
>> This would introduce syntax that can be similarly 
>> misunderstood. AS follows, the similar syntax can be used 
>> today. This code may not be common but it illustrates the 
>> commonality in the syntax, as calling a function doesn't 
>> require the `()` parenthesis.
>> 
>>      https://run.dlang.io/is/Nu6gct
>> 
>>      alias FooDelegate = int delegate();
>>      struct A {
>>          FooDelegate a() { return null; }
>>      }
>> 
>>      void main()
>>      {
>>          A a;
>>          int delegate() t = a.a;
>>      }
>> 
>> 
>> In summation, this is just replacing something that already 
>> was and is repeating past mistake*s*. Ultimately this is just 
>> syntax sugar to avoid 4 characters `() =>`. The justification 
>> is flimsy (along with errors in the DIP)
>
> Please be explicit about errors in the DIP, as just saying 
> there are errors in the DIP is not helpful.

Of course, see the first paragraph for clarification.

>> and the benefit isn't outweighed by the hit to readability and 
>> the potential for that improvement by requiring to just define 
>> a delegate.
>
> It doesn't introduce new syntax, and the point of lazy is to 
> enable lazy evaluation of arguments without having to use 
> delegate syntax. All this does is replace the 'lazy' with the 
> delegate syntax. D already uses this for variadic lazy 
> arguments, and nothing has come up bad about it.

It does replace lazy but it isn't 100% equivalent. The argument 
can be made that lazy doesn't need to be replaced and that's the 
argument I'm making. A wait-and-see approach would be preferred 
here. As once this is implemented it can't be removed nor 
corrected.

I've never seen a variadic lazy being used, I tried searching 
phobos and found no use of it. I'm not even sure under what 
circumstance one would even want to use that. There are bugs in 
the variadic lazy implementation as well (see the discussion 
thread). Just because nothing has come up doesn't mean there 
isn't anything wrong with the implementation, it can just mean 
that no one is using it. From my experience it isn't used very 
often (at all?).

Hope that clarifies some of the points I was making.



More information about the Digitalmars-d mailing list