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

Paul Backus snarwin at gmail.com
Wed Oct 19 18:04:54 UTC 2022


On Wednesday, 19 October 2022 at 17:40:00 UTC, mw wrote:
> That's because you still consider the @mustuse attribute on 
> each class level individually, if you think about the global 
> system analysis as a whole, it will work, and consistently. 
> Yes, what I proposed is transitive closure:
>
>     /--------Base------\
>     |        |         |
> Derived1   Derive2  Derive3
>
> If the programmer only manually marked Derive3.remove() as 
> @mustuse, everything else (Base, Derived1   Derive2  Derive3)'s 
> .remove() method will become @mustuse (as seen by the compiler 
> internally).

The fundamental problem with this on a conceptual level (leaving 
aside implementation issues) is that it completely breaks 
modularity.

Suppose we have the following module layout:

--- base.d
module base;

class Base {
     int fun() {...}
}

void doStuff(Base b)
{
     import std.stdio;

     writeln("Calling b.fun");
     b.fun(); // ok - Base.fun is not @mustuse
}

--- derived.d
module derived;

import base;

class Derived : Base {
     int fun() {...}
}
---

If I now decide to add @mustuse to Derived.fun, in the "derived" 
module, and we apply your proposed global analysis, this will 
cause a compilation error in the "doStuff" function in the "base" 
module!

Note that the "base" module does not have any explicit dependency 
on the "derived" module. It does not import it, or otherwise 
refer to it in any way. In the real world, these two modules 
might even be in separate dub packages.


More information about the Digitalmars-d mailing list