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

H. S. Teoh hsteoh at qfbox.info
Wed Oct 19 18:01:42 UTC 2022


On Wed, Oct 19, 2022 at 05:40:00PM +0000, mw via Digitalmars-d wrote:
> Right now in this discussion thread there are two aspects:
> 1) the language logic, and
> 2) the implementation logic (and current status, including pre-built
> binaries).
> 
> I'm not interested in 2). We need to first make the language logic correct.
> 
> > If indeed @mustuse's purpose is to prevent accidentally discarding
> > important return values, i.e., something marked @mustuse must
> > *never* be silently dropped, then neither (1) nor (2) is acceptable.
> 
> 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).

This means Derived1.remove and Derive2.remove are implicitly @mustuse.
This is bad design; a programmer looking at the code for Derived1 cannot
be expected to know that it is @mustuse.  It introduces a discrepancy
between the surface source code vs. its semantics. Or, put another way,
Derived1.remove has an "invisible" @mustuse that isn't represented in
the source code, and that the programmer cannot possibly know about
unless he looks at *all* instances of .remove in the entire class
hierarchy.

The compiler must enforce that Derived1.remove and Derive2.remove are
explicitly marked @mustuse.  IOW, it must be a compile error for some
instances of .remove to be @mustuse and others not.


[...]
> > > 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!
> > 
> > This is correct.  But it cannot be implicit; if you attribute a
> > method @mustuse somewhere in the inheritance tree, then you must
> > also write @mustuse in every other class in the hierarchy. Otherwise
> > it's not enforceable.
> 
> I'm glad you agree with my global analysis as a whole view here.
> 
> (I want the compiler to propagate and inject such attribute is only to
> save the programmers manual work, by no means it means it's implicit.

Of course it does.  How is it not implicit when Derived1.remove is *not*
marked @mustuse in the source code, but is treated by the compiler as
@mustuse?  The fact that what's in the source code doesn't correspond to
the compiler's internal representation, is the definition of "implicit".

It saves a few keystrokes but introduces long-distance implicit
relationships between distant pieces of code (i.e., Derive3.remove being
marked @mustuse implicitly causes Derived1.remove to be @mustuse, but
the latter is not represented in the source code). This makes the code
hard to understand and reduces maintainability.

Rather, the correct approach should be that all instances of .remove
should be *explicitly* marked @mustuse in the source code. It should be
a compile error for some instances of .remove to be marked @mustuse and
others not marked.


T

-- 
Computerese Irregular Verb Conjugation: I have preferences.  You have biases.  He/She has prejudices. -- Gene Wirchenko


More information about the Digitalmars-d mailing list