that's why I prefer the idea of reciever.(non-method)(args) with the parens mandatory.
That still calls a function without self using method(-like) syntax, so it doesn't make this any less odd. For methods, the first parameter is conceptually special, for functions it is not. This proposal suddenly makes the first parameter of all functions, including existing ones, special as only that parameter can become a receiver with this syntax. That feels like a pretty fundamental shift in how APIs are expected to look, and I am not convinced it's a good idea.
I personally don't really see self as special in any way, except that it allows you to skip the type annotation in the function definition. In fact, my wishlist for a perfect post-rust language includes somehow making Dlang-style UFCS play nice with traits.
I think allowing foreign_type.(my_helper_fn) instead of abusing traits for extension pattern is also a worthy case.
This is a really good point. But this is not how this felt when I did my own testing with the fake Pipe trait (which I did in preparation for this post) – in my testing, the first argument was already, more often than not, a natural receiver.
To verify this, I did the “scientific”:[1] opening random open source projects, taking a look at random files, and looking at the contained function signatures. What I found is that consistently: (1) there are hardly any public functions relative to the number of methods; (2) almost all functions have very low arity; (3) from the functions with at least two arguments, most were either clearly not suitable to be in method form, or the first argument was a reasonable receiver.
You are welcome to test this yourself,[2] but from this small test I'm pretty confident in saying that existing code more often than not will be compatible with this "pipe" operator naturally. This means that, rather then “shifting” the expected order of arguments, one of the implications of adding such syntax might very well be that free functions will just become more abundant:
Not really, but actually this methodology is valid if done properly. ↩︎
If you do, remember that to get a good picture from few examples the sampling should be as random as possible. ↩︎
Note that we have several associated functions on Deref types that are method-like (taking this: & [mut] Self as their first argument), but not actually methods, because they would be kind-of-ambiguous with methods on the Deref::Target type.
Common examples include: Arc::clone, Box::clone, Box::leak, Ref[Mut]::map.
Allowing free functions in the proposed syntax would allow these to get a more method-like feel.
Unless I’m missing something, tap’s pipe is essentially this for a single argument, which means it effectively adds it to the core language with special syntax. Is that a compelling enough sell on its own? If not, the real question, I think, is whether there is enough need for supporting multiple arguments to warrant special syntax.