4 ms·
What a shocker. Allowing f(x,y) to find x.f(y) is nearly useless; and allowing x.f(y) to find f(x,y) is scrapped. Even though D can do the latter just fine. I
by byuu 11y ago
What a shocker. Allowing f(x,y) to find x.f(y) is nearly useless; and allowing x.f(y) to find f(x,y) is scrapped. Even though D can do the latter just fine.
I can already write all my class functions as f(x,y) functions, if I want my code to look like C.
The whole point of UFCS is to work around the obnoxious fact that classes are closed after their initial definitions(*). Only the latter can help with that. So when I use a string library that has a reverse function, I can say string.reverse(), but when it then is missing a lowercase function, my only option is to create lowercase(string) myself. And now my usage of the string class is a mixture of global function versus member function calls. No consistency at all. And when I start trying to chain them, it turs into a complete disaster, eg:
split("\n", lowercase(string.reverse().rtrim("#"))).strip();
//vs
string.reverse().rtrim("#").lowercase().split("\n").strip();
And this is why library writers end up trying to pack the kitchen sink into their classes. Or you end up with things like std::string that, out of the box, are basically just glorified memcpy classes (no trim, no transform, no string replace, no tokenization, no parsing [eg string->int], etc etc.); and thus you end up with countless people making their own string classes anyway.
UFCS could have even allowed extending built-in types in extremely useful ways. I could have declared "BitRef int::bits(int lo, int hi)", and allowed for eg "int x; if(x.bit(7)) x.bits(2,3) = 2;" instead of "if(x & 0x80) { x |= 0x08; x &= ~0x04; }" -- that may look a bit weird, but anyone who's done a ton of bit-twiddling will certainly appreciate the potential there for cleaner code.
I really hope D can get the GC completely out of the standard library and get a command-line switch to disable it. Then I can seriously consider switching languages.
(*) we could actually solve this problem with external class functions, but I'm sure they're even less willing to go for that. But imagine if you had:
class Integer {
...
int square() const { return x * x; }
int self() const { return x; }
private:
int x;
};
extension int Integer::cube() const { return self() * square(); }
int main() {
Integer x = 5;
print("{0}, {1}\n", x.square(), x.cube());
//prints 25,125
}
The idea would be that your extension can't access the private/protected state (unless the function is declared a friend of the class.)
You still have the ability to extend classes with useful functions as needed, and the encapsulation principles aren't violated: no internal state can be accessed, the functions can't be virtual so you won't need to modify the vtable definition, etc.
- 72deluxe 11y agoI know they don't have algorithms are part of the string class in C++ but that's because you can just treat it as any other container and run functions against it, like you would on a vector etc. That's how I saw the string class anyway?