proposal: Go 2: alternate function call syntax to improve ergonomics of chainable top-level functions #47680
Labels
FrozenDueToAge
LanguageChange
Suggested changes to the Go language
Proposal
Proposal-FinalCommentPeriod
v2
An incompatible library change
Milestone
This is a random idea that popped into my head suddenly. I'm not entirely sure that I like it, but I thought that I'd put it out there and see what people think. At the very least, maybe it'll get some more proposals going to try to fix the issue that I'm trying to solve here.
Problem
One of the features purposefully left out of the generics design is adding new type parameters in method declarations. In other words,
func (t *SomeType) SomeMethod[T any](v T)
is illegal. As the design draft pointed out, the only real functionality difference between methods and top-level functions in Go is in interface satisfaction, and how methods that can add new type parameters would affect satisfaction has a lot of subtle implications. One solution that has been proposed is that those methods simply shouldn't count at all towards interface satisfaction, but then the question is why bother making them methods at all.Unfortunately, top-level functions are not always ideal. While it's true that there's no functionality difference outside of interface satisfaction, certain common systems, such as iterators, lend themselves towards APIs that are very awkward as top-level functions. Under the current generics proposal, iterator transformation functions, such as map and filter, would have to be implemented as top-level functions, but top-level functions for iterator chaining can be very messy.
One option is to call those functions one after another with large numbers of intermediary variables:
This is very messy, as well as error prone. Some variable have to be new because they have new types, but some can be the same, and naming all of them something useful is a whole extra thing to worry about pointlessly. An alternative is to nest the calls, but that's actually worse:
Proposal
The idea is simple: Add a syntax, probably in the form of a new operator, that allows top-level functions to be chained like methods. It would be nothing but syntax sugar.
An example syntax for this might look like
a() -> b()
. This would be equivalent tob(a())
. With this syntax, the result of the expression on the left is passed as the first argument of the function call on the right. All other arguments are handled normally. In other words,b(a(1, 2, 3), 8, 9)
could also be written asa(1, 2, 3) -> b(8, 9)
. This usage would make the syntax compatible with method expressions, as well as the majority of functions that already exist due to standard argument order conventions, and allow functions written with this syntax in mind to follow a standard argument ordering as well.Using this syntax, the above admittedly-contrived iterator example could be rewritten as follows:
It should be noted that this would only be legal if the expression on the left side has at most one return. A multi-return version could be implemented, but this proposal is only for a single-return-only variant.
Conclusion
Like I said above, I'm not entirely sure about this idea myself. One particular problem that I have with it is the namespacing required when chaining to an imported function, but I thought that I'd at least propose it as the lack of additional type parameters on methods is my biggest issue with generics as currently proposed, and this could solve that by simply sidestepping the issue completely.
Another potential issue that I have with this is that if it is decided later to try to allow methods with new type parameters that do satisfy interfaces, this will probably feel fairly redundant, but it could still be useful as pseudo extension functions.
Feel free to leave a thumbs-down if you think that it's too weird.
Language Change Template
Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced.
What other languages do you have experience with?
C, Ruby, Python, Kotlin, Java, Rust, JavaScript, and a few others.
Would this change make Go easier or harder to learn, and why?
Slightly harder due to there being an extra syntactic detail to learn, I suppose, but the idea's pretty straightforwards.
Has this idea, or one like it, been proposed before?
I don't think so.
Who does this proposal help, and why?
Anyone trying to write a complicated API that lends itself to chained function calls but that has to be written as top-level functions, It also allows for what are essentially extension methods by way of making top-level functions behave like methods, allowing, for example, for a third-party package to implement some new middleware iterator function and for it to integrate well into existing systems.
What is the proposed change?
Add a new syntax sugar that allows the first argument to any regular, top-level function to be specified before the name of the function. One possibility is that of an
->
operator that does just that, makingb(a(1, 2, 3), 8, 9)
completely equivalent toa(1, 2, 3) -> b(8, 9)
.Is this change backward compatible?
Yes.
Show example code before and after the change.
See above.
What is the cost of this proposal? (Every language change has a cost).
Can you describe a possible implementation?
Nope.
Do you have a prototype? (This is not required.)
Nope.
How would the language spec change?
It would need a new section under function call syntax that would describe this syntactical feature.
Orthogonality: how does this change interact or overlap with existing features?
It complements function call syntax by allowing for certain patterns that are currently unfeasible due to incredibly awkward ergonomics.
Is the goal of this change a performance improvement?
No.
Does this affect error handling?
As currently proposed, no, not really, but a variant could allow for chaining functions with multiple returns and then it could be useful.
Is this about generics?
It was inspired by an issue that came up in the generics design, but it doesn't have anything to do with generics directly itself.
The text was updated successfully, but these errors were encountered: