Command–query separation principle [re: @mustuse as a function attribute]

mw mingwu at gmail.com
Wed Oct 19 16:31:59 UTC 2022


On Wednesday, 19 October 2022 at 13:17:00 UTC, Paul Backus wrote:
> On Wednesday, 19 October 2022 at 08:35:21 UTC, mw wrote:
>> On Wednesday, 19 October 2022 at 08:29:20 UTC, mw wrote:
>>> OK, I saw what they are talking about.
>>>
>>>
>>> Basically, in Eiffel, all query @mustuse.
>>>
>>> Here in D @mustuse is an remedy add-on, which can be 
>>> specified by the programmers in the middle of the inheritance 
>>> tree, the question is what if the pointers got casted either 
>>> upwards or downwards?
>>>
>>> (It's too late for me here today.)
>>>
>>
>> My gut feeling is: make it simple, if a method is marked as 
>> @mustuse anywhere in the inheritance tree, that method in all 
>> the class levels of the inheritance tree became @mustuse!
>
> The correct rule is exactly what Mike quoted from my email 
> discussion with Walter: implicit conversions may add 
> `@mustuse`, but can never remove it.
>
> From this, it follows that
>
> * a `@mustuse` method cannot override a non-`@mustuse` method.
> * a `@mustuse` class/interface cannot inherit from a 
> non-`@mustuse` class/interface.
>
> In other words, you cannot introduce `@mustuse` in a derived 
> class; it must be present in the base class.
>

Let's concentrate on only @mustuse as a function attribute for 
now, since I have not checked the exact semantics of  @mustuse as 
a type annotation.


There are two directions to propagate the attribute in the 
inheritance tree, upwards and downwards. We all agree on the 
direction of downwards propagation to the derived class methods, 
I.e in your expression: "may add `@mustuse`, but can never remove 
it".

Now I only need to convince you the other direction to upwards is 
also needed:

It's actually quite simple, let us consider the original example 
which started this discussion, and do this exercise:



class AbstractPaths {
   AbstractPaths remove (int I) {...}
}


class Paths : AbstractPaths {
   @mustuse
   AbstractPaths remove (int I) {...}
}


AbstractPaths absPaths = new Paths();

absPaths.remove(i);  // shall @mustuse be enforced here on 
absPaths?


Let's step back, and ask why we want to introduce @mustuse in the 
beginning?
The purpose of the annotation is to help programmers do not 
discard important return values accidentally as what the OP's 
author has experienced disaster.

Then the answer is very clear: we do want @mustuse be enforced on 
the absPaths.remove(i) call in the above example!

Otherwise, it will be a loophole in the @mustuse as a function 
attribute logic, which defeat its purpose.

Convinced?


And as a consequence, all the AbstractPaths' derived classes' 
remove(i) method now must have this annotation ( injected by the 
D compiler, the programmer does not have to manually add it in 
all the places). That is why I say:

if a method is marked as @mustuse anywhere in the inheritance 
tree, that method in all the class levels in the whole 
inheritance tree became @mustuse!



More information about the Digitalmars-d mailing list