templating opEquals/opCmp (e.g. for DSL/expression templates)
Rubn
where at is.this
Wed Feb 13 01:24:45 UTC 2019
On Wednesday, 13 February 2019 at 00:56:48 UTC, H. S. Teoh wrote:
> 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
Always hear that D is somehow better than C++ for operators but
it isn't in quite a few places already.
int a;
auto c = (a) <= 0; // ok
auto d = (a) => 0; // not ok
For some reason Walter thought in D if you overload the "+"
operator you couldn't make it not commutative?? Still never got a
reply to that, so I'll just assume he didn't know what
commutative was. Yes you can make "a + b != b + a" be true quite
easily.
Then you have things like "min" where you can do:
foo( a /min/ b );
To get the "min" value between a and b. I guess you could use
this as an example of why not to allow. But at the same time,
we're already pretty much there. That includes "==" operator in
that. So the comparisons operators aren't even consistent.
More information about the Digitalmars-d
mailing list