templating opEquals/opCmp (e.g. for DSL/expression templates)

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Feb 13 00:56:48 UTC 2019


On Tue, Feb 12, 2019 at 11:24:54PM +0000, Olivier FAURE via Digitalmars-d wrote:
> On Tuesday, 12 February 2019 at 13:22:12 UTC, Nicholas Wilson wrote:
> > Oh well, more things to discuss at dconf. That whole thread seems to
> > be Walter either being stubborn or not getting it (or both).
> 
> Or your proposal has drawbacks you're underestimating.
> 
> This community really needs to stop defaulting to "We could totally
> use my awesome feature if Walter wasn't so stubborn" whenever
> discussing language changes.

Frankly, I think it's a very good thing that in D comparison operators
are confined to opEquals/opCmp.  If anything, I would vote for enforcing
opEquals to return bool and bool only.

The reason for this is readability and maintainability.  Symbols like <=
or == should mean one and only one thing in the language, and should not
be subject to random overloaded interpretations. Being built-in
operators, they are used universally in the language, and any
inconsistency in semantics hurts readability, comprehension, and
maintainability, such as C++'s free-for-all operator overloading, where
any piece of syntax can have wildly-divergent interpretations (even
completely different parse trees) depending on what came before.  For
example, recently I came up with a C++ monstrosity where the lines:

        fun<A, B>(a, b);
        gun<T, U>(a, b);

have wildly-different, completely unrelated *parse trees*, as an
illustration of how unreadable C++ can become.

Yes, it's extremely flexible, yes it's extremely powerful and can
express literally *whatever* you want it to express. It's also
completely unreadable and unmaintainable, because the surface structure
of the code text becomes completely detached from the actual underlying
semantics. I don't even want to imagine what debugging such kind of code
must be like.

Operator overloading should be reserved for objects that behave like
arithmetical entities in some way.  And especially comparison operators
should not have any other meaning than the standard meaning.  It should
be illegal to make == and <= mean something completely unrelated to each
other.

If you need to redefine comparison operators, what you want is really a
DSL wherein you can define operators to mean whatever you want.  The
usual invocation of such a DSL as a compile-time argument, say something
like this:

	myDsl!'a <= b'

contains one often overlooked, but very important element: the
identifier `myDsl`, that sets it apart from other uses of `<=`, and
clearly identifies which interpretation should be ascribed to the
symbols found in the template argument.

See, the thing is, when you see a random expression with arithmetical
operators in it, the expected semantics is the computation of some kind
of arithmetic objects producing an arithmetical result -- because that's
what such expressions mean in general, in the language.  It's abusive to
overload that to mean something else entirely -- because there is no
warning sign to the reader of the code that something different is
happening. When you see a line like:

        fun<A, B>(a, b);

and then somewhere else a line like:

        gun<T, U>(a, b);

the actual behaviour of the code should be similar enough that you can
correctly guess the semantics.  It should not be that the first line
instantiates and calls a template function, whereas the second is
evaluated as an expression with a bunch of overloaded comma and
comparison operators.

Similarly, it should not be the case that:

	auto x = a <= b;

evaluates a comparison expression and assigns a boolean value to x,
whereas:

	auto y = p <= q;

creates an expression object capturing p and q, that needs to be called
later before it yields a boolean value.

With a string DSL, that little identifier `myDsl` (or whatever
identifier you choose for this purpose) serves as a cue to the reader of
the code that something special is happening here.  For example:

	auto y = deferred!`p <= q`;

immediately tells the reader of the code that the <= is to be understood
with a different meaning than the usual <= operator. Just as an
expression like:

	auto dg = (p, q) => p <= q;

by virtue of its different syntax tells the reader that the expression
`p <= q` isn't being evaluated here and now, as it otherwise would be.

The presence of such visual cues is good, and is the way things should
be done.

It should not be that something that looks like an expression, evaluated
here and now, should turn out to do something else. That kind of
free-for-all, can-mean-literally-anything semantics makes code
unreadable, unmaintainable, and a ripe breeding ground for bugs --
someone (i.e., yourself after 3 months) will inevitably forget (or not
know) the special meaning of <= in this particular context and write
wrong code for it.


P.S. And as a bonus, a string DSL gives you the freedom to employ
operators not found among the built-in D operators, for example:

	auto result = eval!`(f∘g)(√x ± √y)`;

And if you feel the usual strings literals are too cumbersome to use for
long expressions, there's always the under-used token strings to the
rescue:

	auto result = eval!q{
		( (f ∘ g)(√(x + y) ± √(x - y)) ) / |x|·|y| +
		2.0 * ∫ f(x)·dx
	};

The `eval` tells the reader that something special is happening here,
and also provides a name by which the reader can search for the
definition of the template that processes this expression, and thereby
learn what it means.

Without this little identifier `eval`, it would be anyone's guess as to
what the code is trying to do. Throw in C++-style SFINAE and Koenig
lookup, and what ought to be a 10-second source tree search for an
identifier easily turns into a 6-hour hair-pulling session of trying to
understand exactly which obscure rules the C++ compiler applied to
resolve those operators to which symbol(s) defined in which obscure
files buried deep in the source tree.


T

-- 
Frank disagreement binds closer than feigned agreement.


More information about the Digitalmars-d mailing list