Casting by assigning to the right ...

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Apr 15 17:22:51 UTC 2020


On Wed, Apr 15, 2020 at 04:56:35PM +0000, Manfred Nowak via Digitalmars-d wrote:
[...]
> But in Dlang even unchanged operators have no fixed meaning.
[...]
> One might recognize, that there are no restrictions for structs.
> Therefore it is legal for an instance `lhs' of a struct to write
>     lhs = rhs;
> and thereby de facto assigning to the variable rhs.

This is precisely the kind of operator overloading abuse that Walter
does not like, and which led to the ==, <, >, etc., operators not being
overloadable individually, but only via opEquals and opCmp, as a measure
to ensure some level of consistency in the meaning of the operators.
It's not a perfect measure, of course, and the spec does not prevent
every possible case of abuse that fulfills the letter of the spec but
not the spirit, but that's only because it will also prevent useful
applications of operator overloading, rather than an endorsement of this
sort of operator abuse.

Custom operators have repeatedly been proposed, and Walter's stance has
always been that if you need unusual operators you should just use a DSL
in a string mixin instead of altering the meaning of the language's
surface syntax.

Certainly, it's *possible* to overload opAssign such that `lhs = rhs;`
assigns to rhs instead of lhs, but this sort of operator abuse leads to
code becoming unreadable and unmaintainable because nobody knows what
the surface syntax means anymore, like the hellhole of C++'s
free-for-all, every-man-for-himself wild-wild-west operator overloading
abuse where the following code can have completely divergent *parse
trees*:

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

Think you know that these two lines mean?  Think again. See them in
context:

	// Totally evil example of why C++ template syntax and
	// free-for-all operator overloading is a Bad, Bad Idea.
	#include <iostream>
	struct Bad { };
	struct B { };
	struct A {
		Bad operator,(B b) { return Bad(); }
	};
	struct D { };
	struct Ugly {
		D operator>(Bad b) { return D(); }
	} U;
	struct Terrible { } T;
	struct Evil {
		~Evil() {
			std::cout << "Hard drive reformatted." << std::endl;
		}
	};
	struct Nasty {
		Evil operator,(D d) { return Evil(); }
	};
	struct Idea {
		void operator()(A a, B b) {
			std::cout << "Good idea, data saved." << std::endl;
		}
		Nasty operator<(Terrible t) { return Nasty(); }
	} gun;
	template<typename T, typename U>
	void fun(A a, B b) {
		std::cout << "Have fun!" << std::endl;
	}
	int main() {
		A a;
		B b;

		// What do these lines do?
		fun<A, B>(a, b);
		gun<T, U>(a, b);
	}

You can argue all you want that the programmer ought to read and
memorize all 1400+ pages of C++ spec plus however many hundreds of local
project worth of docs in order to know what he's doing, but at the end
of the day, this is just a huge waste of time and effort just so that
the original code author can save a few keystrokes.  The technical debt
incurred is completely disproportionate with the meager benefits, and
IMO makes no sense.  Why go out of your way to make your code harder to
read and maintain -- as if programming itself isn't already complex
enough that basically every project has hundreds and thousands of bugs
that will require so much effort to fix -- when you could just spend
another 1/2 a second to type a few more keystrokes and make the code
immediately understandable to anyone with a competent grasp of D?

(Of course, if this is a personal project, then sure, go crazy and do
whatever you want, I don't care.  Just be warned that the code *will* be
completely unmaintainable by anyone else -- and by "anyone else" I mean
yourself after 5 months. :-P  Speaking from experience here, btw. I've
been down that road, and I can tell you that it does not lead to a good
place, regardless of all initial appearances.)


T

-- 
In order to understand recursion you must first understand recursion.


More information about the Digitalmars-d mailing list