Oh great. More magic syntax that makes one thing look and behave like another, while not actually being that other.
Every time C++ gets new features, I feel like the designers must never have looked at a C++ core dump, trying to find out what actually happened. The source code bears practically no relation to what actually happens.
The C++ designers should perhaps stop trying to make their language into Common Lisp. You can only fake dynamic behaviour so far until it breaks.
I really don't understand this kind of bash every time C++ gets some cool new feature. A developer that does NOT want to use a feature can always not use it. But why should we deny it to those who are interested and use it at their own risk?
C has added few features worth a damn in 15-25 years. It hasn't even bothered to augment the standard library, which is just dire in terms of API design, capability and performance. Some of C++s worst stdlib components are modelled on libc (e.g. streams and locales)
It fragments the ecosystem. A developer that does not want to use a feature gets limited in its choice of libraries. It makes it harder for people to contribute to other C++ projects (experience can not be directly translated since different aspects of the language might be used).
Exceptions are a good example of that: you can't use a library that's built around exceptions for error reporting if you don't want to use exceptions in your own code.
Also, unless I have strong feeling against some feature I may spend unnecessary time thinking what syntax style should I be using rather than more important stuff. I avoid it, but it still happens to me sometimes unconsciously and I hate it.
Not to mention one more point to argue over coding style.
The problem is that there is no way to reduce complexity in a programming language that is serious about backward compatibility. C++ now has close to 30 years of non-trivial additions built on the craggy basis of backward compatibility with C and its own feature set.
In the early 1990s, it was complex. The Annotated Reference Manual that was used as the de facto standard was full of edge cases and detail that required a great deal of study. No other language since has approached that level of complexity and I'm talking about what it was like before templates.
I sincerely believe that C++'s downfall is underway and it comes from a very simple fact - it's increasingly hard (read impossible) for new C++ programmers to go from zero knowledge of the language to being able to read and maintain modern C++ programs that have taken full advantage of the feature set over the past decades.
There's a saying, "If you want someone to feel good, hurt him and then stop".
C++ is a classical example of this approach, every time C++ committee fights warts it has created, it is perceived like a "cool new feature".
What's exactly cool about this syntax? Is it that C++ can now mimic syntax of truly object-oriented languages better? What's the point if nothing has changed under the hood?
As mentioned elsewhere, consider exception handling, or use of Boost or STL--in many cases, libraries will leak such implementation details during stack traces or interfaces or what have you.
From a coding standpoint, using the hottest new sexy library can mean breaking compatibility with older compilers. This might be surprising, but a lot of times shops still use old versions of compilers because they're a known evil.
For all these cool new features, consider that C++ has yet to standardize on a standard library file system interface (though one is, kinda, in the works). It only just introduced hash maps into the core standard library.
It still does not deign to handle time in a useful fashion: Javascript gets this right out of the box, whereas C++ doesn't even have a way of parsing or generating an ISO-8601 datetime string without having to pull in Boost.
~
The language seems to have abandoned all of the programmers that need to get actual work done, which is fine, because they've all moved to more productive languages like Java, Python, Ruby, Javascript, Go, or PHP.
Consider that someob.foo(other args) in C++ is already effectively syntactic sugar for <address of foo as function pointer>(someob, other args) where <address of foo> is either obtained by statically resolving which function "foo" refers to, or dynamically looking it up in "someob"'s vtable with an indirection.
What we've always had is two syntaxes that results in pretty much the same code (the same for non-virtual member functions and freestanding functions, and slightly different for virtual member functions) that have mainly different by lookup.
This proposal is basically suggesting to reduce the gap by making the "dot syntax" closer to a superset of the function call lookup.
I don't see how that is any more confusing than any number of basic C++ semantics, and it has some fairly substantial benefits, in that it e.g. allows people to write libraries to "extend" the apparent interface of a class without forcing users to use two entirely different syntaxes.
Just being able to turn instances of baz(bar(foo(ob))) into ob.foo().bar().baz() would be worth it to me.
I'm not a C programmer, but in general, I think this is a great idea. I really wish that it could be added to a high-level language. However, I think it's fundamentally incompatible with some of the features of high-level languages, such as dynamic typing, first-class functions, partial application, parametric polymorphism, structural typing/extensible records...
Why is dynamic typing a feature of high level languages? I know quite a few high level languages that are statically typed. And what does C have to do with C++?
I'm uncertain what languages other than C++ you think would benefit much from this.
The reason C++ would benefit from this in the first place is because of a combination of its lack of higher level features (e.g. enabling extension/mixing in functionality in less painful ways) and its "C legacy" in the form of vast amounts of non-OO code.
E.g. consider a language like Ruby, which has most of what you list as features of "high-level languages". For Ruby this would be very weird to begin with, because Ruby does not have non-member functions - everything calleable in Ruby is a method; what looks like free-standing functions in Ruby are functions on an object whose eigenclass is the implicit outermost scope.
What we'd do instead in Ruby is to put "function like" methods in a module that we can include in whatever objects we want.
But you can in fact easily achieve what this proposal does in Ruby.
Many languages that are not primarily object-oriented. E.g. it was proposed in Rust [1]. It could also be added to many other functional languages, as often the "object-oriented" function call syntax looks more natural (when the first parameter is "special", the object of the call) than the "mathematical" function call syntax (appropriate for when parameters are equivalent, e.g. `max()` or `dot_product()`).
Even though it makes sense with regard to the way operators work currently (they can be defined as members, or as free functions), it looks like a breaking change to me
struct A {
double magic(int);
};
string magic(A&, int);
is currently legal code. Under this proposal they'd effectively be overloads with different return types, which is is a compile-time error, unless I'm missing something.
In order to maintain compatability, magic(a,b) would have to call the nonmember function and a.magic(b) would call the member function. Ideally, if you were using this feature, you would enable a warning that tells you if there's a collision like this in any scope. Otherwise, your code would continue to work just as it already does.
Unless I'm missing something, I don't think this proposal would break anything, and while the edge behavior for collisions is awkward and unfortunate, at least its easy to identify and avoid
Apple's Swift makes more sense with its curry feature. If you have class 'C' with method 'm' taking argument 'x', you can call that method either like c.m(x) or C.m(c, x) because C.m is automatically provided closure taking instance of C (typically as "self") and argument x.
Most importantly, there is no magic going on with namespaces. Method 'm' still belongs to its class's namespace.
First of all, don't flame me too much because if you follow my advice it wouldn't be C++ anymore. This is not a suggestion about the post but an invite to think.
I always wondered why somebody designs object oriented languages with functions and eventually run into this kind of problems.
An answer might be that they're not willing to wrap fundamental types with classes, even if it would be only syntactic sugar that the compiler can easily unwrap without losing efficiency in the generated code. Probably this is not the only reason, as for the case of Python's len() and the like. Python's designer actually wanted functions to make sure all classes had the same methods, exported as functions.
Furthermore there might be historical reasons, in the case of C++ to make it look like C. Too many differences can scary people away. Think about what it took to make Objective-C successful, the almost impossibility to use any other language to program on a given platform.
Finally, there is convenience, as in void fn(int x) {...} main() { ...; fn(x); ...} By the way, Ruby solves that by adding fn to the Kernel class so it allows the same convenient syntax in a complete OO world.
Anyway, an OO language in which everything is an object doesn't suffer from the kind of problems the post is trying to solve. Years ago I went to Ruby from Java and a little of C++. I was happy not to have that kind of int vs Integer duality. Then I looked at Python and found it a little bit weird with functions mixed to methods, but that's the way it is.
So my advice is as radical as unrealistic in the case of C++: if you want to change the language, make it use methods where it still uses functions. The compiler will deal with that. Remember that when you'll design your next OO language :-)
In early-bound, single-dynamic-dispatch languages it makes a small amount of sense, as x.fn() is the only syntax for a dynamic dispatch against object x and fn(x) uses at most compile-time information to determine what fn gets called.
I'd also prefer "intersect(s1, s2)" if C++ had multimethods...
But to follow the line of Bjarne's advice (I've also heard same from Stepanov): One should prefer non-member functions. i.e. there must be some real reason for some function to become a member of a class.
The problem with non member functions is that, once you start preferring them, you are basically writing C. They are contagious. There's very little reason for class members unless you want inheritance or polymorphism. Scott Meyers has also promoted them:
With non member functions declared in the header and defined in a .C file, and suddenly you don't have RIAA any more or exceptions. Your .C files end up being your division of concerns, not classes.
The only time you end up using any C++ features is interacting with a library that prefers member functions.
This idea that 'files' are an organizing principle is bankrupt. In an editor I don't even know what file a method is in. Including header files is an annoyance, and should be automated.
In fact all code should be in a database, not a file, so changes can be tracked and attributed properly. So a tool can distinguish between 'lines being edited' and 'one function deleted and another added'. And so on.
So what organizing principle is left, since files are largely invisible during the coding process? Well, classes spring to mind.
I guess that wasn't clear. I surf methods, not by opening files, but by going directly to the definition or declaration as needed. Sure its in some file, but I deal with 1000 methods and sure don't make any effort to memorize where they are - since its entirely irrelevant to the correctness of the code.
To make it clear: I favor factoring APIs into classes.
I think factoring api's into classes makes a lot of sense
too, I think this idea of preferring non member functions undoes a lot of the functionality and team building cohesiveness that C++ offers.
It's strange that C++ luminaries like Stepanov, Meyers and Stroustrup favour non member functions.
Well, the key thing is the `reason` here. In C++ we use RAII, so this is the reason to have a member constructor/destructor functions instead of non-member create_something(...)/destroy_something(...) (aka C style).
But, for instance, `sort` is a good candidate to have outside of a class - and it actually is a global function - std::sort.
When you start preferring non member functions, you tend allocate on the stack.
There are rare items that require heap allocation and other resources. For those you can have classes. What I am saying is that when you prefer non member functions you end up with very few classes.
You don't use classes only to get RAII. They are also useful when you have data with invariants, which the constructor establishes. And of course, if you want to use private data.
there's many ways to implement private data. if you prefer non member functions, a class is a bunch of extra keystrokes to achieve the same as the old fashioned C private data.
if you prefer non member functions to member functions, your private data becomes a global variable or struct in a file, and the private data is not accessible by getter/setter functions that are externalised.
i think you need to read my comment again because "private:" in a class is not the only way to implement private data. you are only repeating what the parent commenter said.
Well, to follow your line - virtual functions can also be implemented with some function pointer stuff within C. We're not discussing implementing various C++ stuff in C, we're talking about C++ itself.
be that as it may, it requires more keystrokes to implement virtual functions in C than it does in C++.
however, it requires less keystrokes to implement private data in C, than it does to create a class to store private data.
therefore it's questionable, if you are preferring non member non friend functions, whether you should use classes when you want private data.
i refute the idea that private data is a reason to use classes, if you are preferring non member non friend functions as advocated by Stroustrup, Meyers and Stepanov.
as i said already
> There's very little reason for class members unless you want inheritance or polymorphism.
The interesting part to me with this, is that for the last 20 years or so there's been a certain strain of C++ development, spurred on by increasing template/meta-programming support, that have advocated for moving more code to non-member functions, to benefit from improved reuse.
But this has been awkward because it means calling conventions may often be different and functionality that's "mixed in" this way seem far less integrated than functionality that's brought in via inheritance. It's also had the disadvantage that if you ever suddenly needed one of those free-standing functions to depend on internal state of an object, you'd either have to break encapsulation, or break the API (by moving the function into the class), or provide a legacy function to forward to the new API.
This change would make it even more attractive to break out functionality that does not depend directly on the internal state of an object as standalone functions, in effect creating a sort-of "limited class re-opening".
>> This change would make it even more attractive to break out functionality that does not depend directly on the internal state of an object as standalone functions, in effect creating a sort-of "limited class re-opening".
You can already write the functions that way, nobody is stopping you. f(x,y) a function that returns something based on x and y. x.f(y) a method where object type x does something based on y.
The second form will have access to private members of x and has some notion of the object doing something. The first form implies that x is just an argument to the function.
What I see is C++ going from object oriented to functional - in other words following whats trendy.
That said, I think they should take all the lessons learned doing high performance implementations of C++ and start over to create a new language that doesn't suck with all the baggage.
The problem is that such an effort would produce a cute academic toy with zero traction in the real world. Or looking at it from the other perspective - would you expect that Clang, Chrome, Mozilla folks just rewrite their projects? What about the core infrastructure code that Google/Bloomberg have implemented in C++?
> That said, I think they should take all the lessons learned doing high performance implementations of C++ and start over to create a new language that doesn't suck with all the baggage.
I would expect some comments on covariance here.
With this you seem to have a magic first parameter x that gets to be dispatched based on the runtime type of x,
while y isn't and gets to be overloaded according to its static type. The dot notation at least conveys some notion of x being looked at in a different way than y.
This idea _almost_ sounds like a joke. A member function is indeed different from a non-member function precisely because it is not a member of the set of functions responsible for maintaining the object's invariants. Treating
f(x, y);
as
x.f(y);
only makes sense (to me?) if all other things being equal, if f(x, y) is a friend of the class. And picking the member function form over the non-member function in the caller's scopes seems like a step away from encapsulation. Prefer non-member functions. Scott says so!
Consider that under the hood, they result in the same code (unless f is a virtual member function, in case it'd be equal to x.<vtable ptr>[vtable offset of f](x,y)).
For non-virtual member functions they differ in syntax and lookup, not implementation. In effect they provide two different syntaxes for something that is _very_ often the same thing, namely executing a piece of code that logically "belongs" to and operates on the first argument.
E.g. look at the C standard library, and consider how many of the functions there operates on the state of the first argument.
This change would let you write an f() that does not have access to the internals of x, yet can still be called the same way as the member functions, providing increased uniformity and making the choice of member function vs. non-member function more of an implementation choice by letting you reduce the impact on the documented API:
> And picking the member function form over the non-member function in the caller's scopes seems like a step away from encapsulation. Prefer non-member functions. Scott says so!
Preferring non-member functions is exactly why I think this seems interesting. The awkwardness of splitting an interface so people have to remember what is a member function and what is a non-member function, and having to decide how it should be split, easily results in a lot of stuff ending up as member functions when it doesn't have to be.
If it's transparent to the user, there are fewer reasons to prefer member functions, not least because "promoting" a non-member function to a member function if you later need access to more internal state becomes easier.
I don't agree. The _signature_ should indicate whether a function has access to a class's internals or not, but the caller shouldn't have to care. This is already true in C++ today: when we write f(x), f might be a normal free function or it might be a friend of x.
Viewed from that angle, this proposal makes C++ more consistent, not less: it breaks a needless dependency between syntax and semantics.
The traditional C++ syntax to me is the most clear, hiding functions under passing the hidden this pointer just brings the entire thing seemingly back to C.
I don't know what it's chances of ratification are -- it does seem like an aggressive change -- but I disagree that it doesn't add anything. This would actually fix one of the biggest problems I have with large C++ development: non-open classes.
Think of a widely used class -- for example lets use std::string. Maybe for your program there are some missing methods that would really handy to add, like maybe .urlencode(). Today in C++ you can't though: if the method isn't mentioned in the original definition of std::string in its header file it's permanently missing.
This leads to pressure to make a custom version of the class, either inheriting or encapsulating the original. This seems to work OK but then can lead to problems if you later want to interface with someone else's library written in C++. For instance you might have "class mystring : public std::string" and they have "class theirstring : ..." now you have to explicitly convert between them even if data-wise they're 100% compatible.
This proposal neatly solves this problem -- you could just implement string extensions as a function and still be able to call them like a regular method. The ability to add methods to other people's types promotes type reuse.
The same result could be achieved by introducing a smaller change to the language that doesn't completely redefine the way existing functions are evaluated. By using some specific signature (like adding a keyword to the first argument of a function), you could indicate that this function may be used as a member method of the first argument. For example, C# uses the keyword 'this' for its extension methods:
public static class Helpers
{
public static string UrlEncode(this string url)
{
//...
}
}
// then later:
string someText = "...";
string a = Helpers.UrlEncode(someText); // valid
string b = someText.UrlEncode(); // valid too
But of course that would mean having to update all the function declarations of the standard library to add the new keyword when needed...
Yes, I agree that there are simpler changes that would fix the problem, and personally I'd root for them. Stroustrup's proposal is still interesting in that it would solve it and unify operator/method dispatch at the same time. It has a certain elegance.
Oh, wow. I know most people dislike C++ language extensions, given how complex the language already is. But as a library author, I truly believe this is the most powerful and useful addition we can add to the language now. I've been wanting this exact thing for years, but unfortunately you can't even do it as a preprocessor due to the complexity of ADL and SFINAE on lookups.
First, I like Herb Sutter's propsal more ( http://isocpp.org/files/papers/N4165.pdf ), as Bjarne's proposal to favor x.f() when encountering f(x) would be a breaking change. However, Herb's suggestion to accept x in any position in f (eg x.f(a, b) could match f(a, x, b)), in order to wrap libc interfaces such as FILE* more easily, I feel is a mistake. I'd rather we keep the complexity of parsing down and write wrapper headers around libc to support the new syntax instead (which is also a wonderful opportunity to get the functions and #defines out of the global namespace.)
Onto utility ... as stated, nearly all of my time writing C++ is spent creating libraries. This is always a huge struggle: you don't want users to have to remember that some functions are class::func() and others are func(class&), and indeed it makes for some really ugly code, eg c(b(a()) instead of a().b().c(). Further, since C++ classes cannot be reopened, it's a real problem to just rely on users to extend your classes to add desired features. Person A makes class fooObject : object, person B makes class barObject : object. But now fooObject and barObject are incompatible, unless you slice back to object, and lose the whole point of your extensions.
Unified function call syntax (UFCS) solves this nicely: member functions are now only those that need access to private state. Your classes are much smaller, which aids in encapsulation. Further, you don't have to code everything but the kitchen sink into your classes anymore, the user can simply add functionality that they need. Or you can offer the extensions piecemeal in separate headers. So not only do we get UFCS, we also get a nicer form of C# extension methods for free.
Further, it's a real boon to IDE auto-completion features to be able to know available functions after object.[function] than it is after function([object]; the former is much easier. Imagine an IDE trying to auto-complete "begin(", for instance. That list is going to be hopelessly long.
Lastly, it will be very interesting to see what rules they put in place around this and primitive types. If you can have square(int&), then you could call int x = 5; int y = x.square(); But even more interesting will be if this is allowed for constants: int x = 5.square(); string y = "hello".toUppercase(); ... there is the potential to allow C++ to be a truly object oriented language where everything is an object, if this is done right.
On that note, I haven't seen much talk around the implementation of f(x), but they definitely need to allow for both x::f() -> f(x&) and x::f() const -> f(const x&). I'm also a little concerned about x->f() -> f(x*), and how that might play in with overloaded operator-> that you find in smart pointer classes.
For those in favor of f(x, y) over x.f(y), please consider the history of ambiguity around operator arguments. strcat is (target, source) whereas rename is (source, target). There are thousands of examples like this. Putting the target before the function call is an absolutely wonderful way to remove the burden of having to remember each and every function's exact ordering. For cases where there is no clear "target", regular function call syntax can still be used, eg intersect(x, y)
But, complexities aside ... I really hope this feature makes it in, and that the naysayers to all language extensions do not ruin this. It will completely change the way I program in C++.
What is a use case in your library code where you have the problem of class::func() vs func(class&)? I'm willing to wager your API is just plain written badly in that case.
It will completely change the way I program in C++.
This is a bug, not a feature.
I kind of think, at this point, Bjarne is trying to get the language to implode once and for all so he can finally get some rest--and only has to take such drastic measures because the community is too enamored with shiny to just let the old dog die.
Take std::string. It is missing majorly useful functions that exist in other languages, such as trim, transform, split/explode/tokenize, replace-by-string (std::string is replace-by-length, which is more like memcpy), etc.
You can add those functions. But you can't add them into std::string. So you get: void trim(string& s, const string& trim);
And now your code is a mix of two styles:
s.append(" Hello, World ");
trim(s, " ");
s.compare(...);
As I've explained, subclassing is dangerous. Two people each add their own functions to their own derived types, and now those types aren't directly compatible without slicing back to std::string.
So the problem, as a class author, is that you want to add all of the functions a user might want, so that they can consistently use the same syntax. So when you write your string class, users can then use it like so:
s.append(" Hello, World ");
s.trim(" ");
s.compare(...);
//or even possibly ...
s.append(" Hello, World ").trim(" ").compare(...);
(You may say the syntax difference doesn't matter, but I disagree strongly: it's burdensome to remember which functions are string::foo(), and which functions are foo(string&); and if I wanted to use foo(string&) exclusively, then I'd go back to writing in C.)
This causes substantial bloat by packing string with a ridiculous number of functions.
Some of those functions are going to be very niche. For instance, I have find/replace/split functions that ignore values inside of quotes, which I use extensively in a cross assembler I wrote. But they're not very useful for my other programs. It would be nice if I could #include <string/extension/quoted-strings.hpp> for the cross assembler, which would contain UFCS functions, and leave this out of my other projects. Smaller compilation times, and easier to create experimental/fluid extensions before committing them to the official class API.
And if a user of my string library decided that they needed additional functions, they too could add them.
I would go so far as to say that UFCS will result in more developers using the C++ standard library containers and types, rather than rolling their own. Which would be a huge win for the language.
Considering that this is already the case in D, it seems like it might be a good idea. Though who knows how much havoc this will cause in older code bases that are riddled with hacks.
I like the idea of unified call syntax. I know D and Nimrod support a system similar to the one proposed here, albeit they were designed with the syntax from the start and avoid the more awkward edge cases.
If it can be proven not to break existing code, I think it would be a positive addition to the language. I worry, however, that there might be some confusion. I imagine seeing x.a().b().c() where you expect c(b(a(x))) would throw you off.
An alternative worth considering is what Kotlin calls extension methods (and I'm sure I've seen the idea elsewhere). The idea is you could declare a member or non-member function to always be called with either call syntax. This eliminates the burden on the programmer of choosing which syntax to use, eliminates a lot of edge cases, but doesn't give the same flexibilty as the unified syntax.
69 comments
[ 2.9 ms ] story [ 142 ms ] threadEvery time C++ gets new features, I feel like the designers must never have looked at a C++ core dump, trying to find out what actually happened. The source code bears practically no relation to what actually happens.
The C++ designers should perhaps stop trying to make their language into Common Lisp. You can only fake dynamic behaviour so far until it breaks.
There, I said it.
Exceptions are a good example of that: you can't use a library that's built around exceptions for error reporting if you don't want to use exceptions in your own code.
Not to mention one more point to argue over coding style.
In the early 1990s, it was complex. The Annotated Reference Manual that was used as the de facto standard was full of edge cases and detail that required a great deal of study. No other language since has approached that level of complexity and I'm talking about what it was like before templates.
I sincerely believe that C++'s downfall is underway and it comes from a very simple fact - it's increasingly hard (read impossible) for new C++ programmers to go from zero knowledge of the language to being able to read and maintain modern C++ programs that have taken full advantage of the feature set over the past decades.
If the language is not complex, then it is the eco-system.
An ecosystem can also be quite complex, specially if it changes often.
Any newcomer will face the exact situation not knowing what to use, what is no longer best practice or what should be avoided.
C++ is a classical example of this approach, every time C++ committee fights warts it has created, it is perceived like a "cool new feature".
What's exactly cool about this syntax? Is it that C++ can now mimic syntax of truly object-oriented languages better? What's the point if nothing has changed under the hood?
As mentioned elsewhere, consider exception handling, or use of Boost or STL--in many cases, libraries will leak such implementation details during stack traces or interfaces or what have you.
From a coding standpoint, using the hottest new sexy library can mean breaking compatibility with older compilers. This might be surprising, but a lot of times shops still use old versions of compilers because they're a known evil.
For all these cool new features, consider that C++ has yet to standardize on a standard library file system interface (though one is, kinda, in the works). It only just introduced hash maps into the core standard library.
It still does not deign to handle time in a useful fashion: Javascript gets this right out of the box, whereas C++ doesn't even have a way of parsing or generating an ISO-8601 datetime string without having to pull in Boost.
~
The language seems to have abandoned all of the programmers that need to get actual work done, which is fine, because they've all moved to more productive languages like Java, Python, Ruby, Javascript, Go, or PHP.
What we've always had is two syntaxes that results in pretty much the same code (the same for non-virtual member functions and freestanding functions, and slightly different for virtual member functions) that have mainly different by lookup.
This proposal is basically suggesting to reduce the gap by making the "dot syntax" closer to a superset of the function call lookup.
I don't see how that is any more confusing than any number of basic C++ semantics, and it has some fairly substantial benefits, in that it e.g. allows people to write libraries to "extend" the apparent interface of a class without forcing users to use two entirely different syntaxes.
Just being able to turn instances of baz(bar(foo(ob))) into ob.foo().bar().baz() would be worth it to me.
That's funny because I thought he was trying to suggest nobody use the . notation any more, while you give a nice example of going the other way.
The reason C++ would benefit from this in the first place is because of a combination of its lack of higher level features (e.g. enabling extension/mixing in functionality in less painful ways) and its "C legacy" in the form of vast amounts of non-OO code.
E.g. consider a language like Ruby, which has most of what you list as features of "high-level languages". For Ruby this would be very weird to begin with, because Ruby does not have non-member functions - everything calleable in Ruby is a method; what looks like free-standing functions in Ruby are functions on an object whose eigenclass is the implicit outermost scope.
What we'd do instead in Ruby is to put "function like" methods in a module that we can include in whatever objects we want.
But you can in fact easily achieve what this proposal does in Ruby.
[1] https://mail.mozilla.org/pipermail/rust-dev/2013-October/006...
Unless I'm missing something, I don't think this proposal would break anything, and while the edge behavior for collisions is awkward and unfortunate, at least its easy to identify and avoid
Most importantly, there is no magic going on with namespaces. Method 'm' still belongs to its class's namespace.
I always wondered why somebody designs object oriented languages with functions and eventually run into this kind of problems.
An answer might be that they're not willing to wrap fundamental types with classes, even if it would be only syntactic sugar that the compiler can easily unwrap without losing efficiency in the generated code. Probably this is not the only reason, as for the case of Python's len() and the like. Python's designer actually wanted functions to make sure all classes had the same methods, exported as functions.
Furthermore there might be historical reasons, in the case of C++ to make it look like C. Too many differences can scary people away. Think about what it took to make Objective-C successful, the almost impossibility to use any other language to program on a given platform.
Finally, there is convenience, as in void fn(int x) {...} main() { ...; fn(x); ...} By the way, Ruby solves that by adding fn to the Kernel class so it allows the same convenient syntax in a complete OO world.
Anyway, an OO language in which everything is an object doesn't suffer from the kind of problems the post is trying to solve. Years ago I went to Ruby from Java and a little of C++. I was happy not to have that kind of int vs Integer duality. Then I looked at Python and found it a little bit weird with functions mixed to methods, but that's the way it is.
So my advice is as radical as unrealistic in the case of C++: if you want to change the language, make it use methods where it still uses functions. The compiler will deal with that. Remember that when you'll design your next OO language :-)
"The functional (mathematical) notation is far older and more general than the object-oriented dot notation."
"For example, I prefer intersect(s1,s2) over s1.intersect(s2)."
But to follow the line of Bjarne's advice (I've also heard same from Stepanov): One should prefer non-member functions. i.e. there must be some real reason for some function to become a member of a class.
Bjarne has wanted multi-methods for decades.
http://www.drdobbs.com/cpp/how-non-member-functions-improve-...
With non member functions declared in the header and defined in a .C file, and suddenly you don't have RIAA any more or exceptions. Your .C files end up being your division of concerns, not classes.
The only time you end up using any C++ features is interacting with a library that prefers member functions.
In fact all code should be in a database, not a file, so changes can be tracked and attributed properly. So a tool can distinguish between 'lines being edited' and 'one function deleted and another added'. And so on.
So what organizing principle is left, since files are largely invisible during the coding process? Well, classes spring to mind.
There will be very few classes in your project if you decide to prefer non member non friend functions as suggested here.
The question being, whether you go along with that suggestion or not?
To make it clear: I favor factoring APIs into classes.
It's strange that C++ luminaries like Stepanov, Meyers and Stroustrup favour non member functions.
But, for instance, `sort` is a good candidate to have outside of a class - and it actually is a global function - std::sort.
When you start preferring non member functions, you tend allocate on the stack.
There are rare items that require heap allocation and other resources. For those you can have classes. What I am saying is that when you prefer non member functions you end up with very few classes.
there's many ways to implement private data. if you prefer non member functions, a class is a bunch of extra keystrokes to achieve the same as the old fashioned C private data.
if you prefer non member functions to member functions, your private data becomes a global variable or struct in a file, and the private data is not accessible by getter/setter functions that are externalised.
Unless you're a fan of `friend` keyword ;)
however, it requires less keystrokes to implement private data in C, than it does to create a class to store private data.
therefore it's questionable, if you are preferring non member non friend functions, whether you should use classes when you want private data.
i refute the idea that private data is a reason to use classes, if you are preferring non member non friend functions as advocated by Stroustrup, Meyers and Stepanov.
as i said already
> There's very little reason for class members unless you want inheritance or polymorphism.
But this has been awkward because it means calling conventions may often be different and functionality that's "mixed in" this way seem far less integrated than functionality that's brought in via inheritance. It's also had the disadvantage that if you ever suddenly needed one of those free-standing functions to depend on internal state of an object, you'd either have to break encapsulation, or break the API (by moving the function into the class), or provide a legacy function to forward to the new API.
This change would make it even more attractive to break out functionality that does not depend directly on the internal state of an object as standalone functions, in effect creating a sort-of "limited class re-opening".
You can already write the functions that way, nobody is stopping you. f(x,y) a function that returns something based on x and y. x.f(y) a method where object type x does something based on y.
The second form will have access to private members of x and has some notion of the object doing something. The first form implies that x is just an argument to the function.
What I see is C++ going from object oriented to functional - in other words following whats trendy.
That said, I think they should take all the lessons learned doing high performance implementations of C++ and start over to create a new language that doesn't suck with all the baggage.
Oh, you mean Rust (programming language)?
For non-virtual member functions they differ in syntax and lookup, not implementation. In effect they provide two different syntaxes for something that is _very_ often the same thing, namely executing a piece of code that logically "belongs" to and operates on the first argument.
E.g. look at the C standard library, and consider how many of the functions there operates on the state of the first argument.
This change would let you write an f() that does not have access to the internals of x, yet can still be called the same way as the member functions, providing increased uniformity and making the choice of member function vs. non-member function more of an implementation choice by letting you reduce the impact on the documented API:
> And picking the member function form over the non-member function in the caller's scopes seems like a step away from encapsulation. Prefer non-member functions. Scott says so!
Preferring non-member functions is exactly why I think this seems interesting. The awkwardness of splitting an interface so people have to remember what is a member function and what is a non-member function, and having to decide how it should be split, easily results in a lot of stuff ending up as member functions when it doesn't have to be.
If it's transparent to the user, there are fewer reasons to prefer member functions, not least because "promoting" a non-member function to a member function if you later need access to more internal state becomes easier.
Viewed from that angle, this proposal makes C++ more consistent, not less: it breaks a needless dependency between syntax and semantics.
Think of a widely used class -- for example lets use std::string. Maybe for your program there are some missing methods that would really handy to add, like maybe .urlencode(). Today in C++ you can't though: if the method isn't mentioned in the original definition of std::string in its header file it's permanently missing.
This leads to pressure to make a custom version of the class, either inheriting or encapsulating the original. This seems to work OK but then can lead to problems if you later want to interface with someone else's library written in C++. For instance you might have "class mystring : public std::string" and they have "class theirstring : ..." now you have to explicitly convert between them even if data-wise they're 100% compatible.
This proposal neatly solves this problem -- you could just implement string extensions as a function and still be able to call them like a regular method. The ability to add methods to other people's types promotes type reuse.
First, I like Herb Sutter's propsal more ( http://isocpp.org/files/papers/N4165.pdf ), as Bjarne's proposal to favor x.f() when encountering f(x) would be a breaking change. However, Herb's suggestion to accept x in any position in f (eg x.f(a, b) could match f(a, x, b)), in order to wrap libc interfaces such as FILE* more easily, I feel is a mistake. I'd rather we keep the complexity of parsing down and write wrapper headers around libc to support the new syntax instead (which is also a wonderful opportunity to get the functions and #defines out of the global namespace.)
Onto utility ... as stated, nearly all of my time writing C++ is spent creating libraries. This is always a huge struggle: you don't want users to have to remember that some functions are class::func() and others are func(class&), and indeed it makes for some really ugly code, eg c(b(a()) instead of a().b().c(). Further, since C++ classes cannot be reopened, it's a real problem to just rely on users to extend your classes to add desired features. Person A makes class fooObject : object, person B makes class barObject : object. But now fooObject and barObject are incompatible, unless you slice back to object, and lose the whole point of your extensions.
Unified function call syntax (UFCS) solves this nicely: member functions are now only those that need access to private state. Your classes are much smaller, which aids in encapsulation. Further, you don't have to code everything but the kitchen sink into your classes anymore, the user can simply add functionality that they need. Or you can offer the extensions piecemeal in separate headers. So not only do we get UFCS, we also get a nicer form of C# extension methods for free.
Further, it's a real boon to IDE auto-completion features to be able to know available functions after object.[function] than it is after function([object]; the former is much easier. Imagine an IDE trying to auto-complete "begin(", for instance. That list is going to be hopelessly long.
Lastly, it will be very interesting to see what rules they put in place around this and primitive types. If you can have square(int&), then you could call int x = 5; int y = x.square(); But even more interesting will be if this is allowed for constants: int x = 5.square(); string y = "hello".toUppercase(); ... there is the potential to allow C++ to be a truly object oriented language where everything is an object, if this is done right.
On that note, I haven't seen much talk around the implementation of f(x), but they definitely need to allow for both x::f() -> f(x&) and x::f() const -> f(const x&). I'm also a little concerned about x->f() -> f(x*), and how that might play in with overloaded operator-> that you find in smart pointer classes.
For those in favor of f(x, y) over x.f(y), please consider the history of ambiguity around operator arguments. strcat is (target, source) whereas rename is (source, target). There are thousands of examples like this. Putting the target before the function call is an absolutely wonderful way to remove the burden of having to remember each and every function's exact ordering. For cases where there is no clear "target", regular function call syntax can still be used, eg intersect(x, y)
But, complexities aside ... I really hope this feature makes it in, and that the naysayers to all language extensions do not ruin this. It will completely change the way I program in C++.
It will completely change the way I program in C++.
This is a bug, not a feature.
I kind of think, at this point, Bjarne is trying to get the language to implode once and for all so he can finally get some rest--and only has to take such drastic measures because the community is too enamored with shiny to just let the old dog die.
Take std::string. It is missing majorly useful functions that exist in other languages, such as trim, transform, split/explode/tokenize, replace-by-string (std::string is replace-by-length, which is more like memcpy), etc.
You can add those functions. But you can't add them into std::string. So you get: void trim(string& s, const string& trim);
And now your code is a mix of two styles:
As I've explained, subclassing is dangerous. Two people each add their own functions to their own derived types, and now those types aren't directly compatible without slicing back to std::string.So the problem, as a class author, is that you want to add all of the functions a user might want, so that they can consistently use the same syntax. So when you write your string class, users can then use it like so:
(You may say the syntax difference doesn't matter, but I disagree strongly: it's burdensome to remember which functions are string::foo(), and which functions are foo(string&); and if I wanted to use foo(string&) exclusively, then I'd go back to writing in C.)This causes substantial bloat by packing string with a ridiculous number of functions.
Some of those functions are going to be very niche. For instance, I have find/replace/split functions that ignore values inside of quotes, which I use extensively in a cross assembler I wrote. But they're not very useful for my other programs. It would be nice if I could #include <string/extension/quoted-strings.hpp> for the cross assembler, which would contain UFCS functions, and leave this out of my other projects. Smaller compilation times, and easier to create experimental/fluid extensions before committing them to the official class API.
And if a user of my string library decided that they needed additional functions, they too could add them.
I would go so far as to say that UFCS will result in more developers using the C++ standard library containers and types, rather than rolling their own. Which would be a huge win for the language.
I'm pretty sure that's not what Bjarne has in mind, though.
http://www.stroustrup.com/multimethods.pdf
If it can be proven not to break existing code, I think it would be a positive addition to the language. I worry, however, that there might be some confusion. I imagine seeing x.a().b().c() where you expect c(b(a(x))) would throw you off.
An alternative worth considering is what Kotlin calls extension methods (and I'm sure I've seen the idea elsewhere). The idea is you could declare a member or non-member function to always be called with either call syntax. This eliminates the burden on the programmer of choosing which syntax to use, eliminates a lot of edge cases, but doesn't give the same flexibilty as the unified syntax.