Would you consider yourself a novice, intermediate, or experienced Go programmer?
intermediate
What other languages do you have experience with?
In order of experience … C#, Smalltalk, Javascript, Java, C, python, Haskell
Would this change make Go easier or harder to learn, and why?
Harder – one more concept to understand.
Has this idea, or one like it, been proposed before?
Yes, because it potentially mitigates problems outlined in several other issues:
proposal: Go 2: Uniform Function Call Syntax · Issue #56242 · golang/go · GitHub
proposal: Go 2: introduce a way of performing function chains · Issue #56283 · golang/go · GitHub
proposal: Go 2: add elixir-like pipe operator · Issue #33361 · golang/go (github.com)
If so, how does this proposal differ?
This proposal is different in implementation. It does not match the solution from the linked issues, but I believe it mitigates the underlying problems.
Who does this proposal help, and why?
go programmers who want to reduce boiler plate code.
What is the proposed change?
interfaces and named aliases would become allowable receivers. The change would just be syntactic sugar for functions that accept this interface.
There are some restrictions:
R1: Just like normal methods, “interface methods” can only be defined within the package the interface is defined. This prevents imported code from potentially breaking or overriding the intended functionality.
R2: interface methods cannot override existing methods. Attempting to cast an object to an interface that it already fulfills or partially fulfills would be a compile time error. This prevents interfaces from becoming self-fulfilling, or overriding expected functionality. The compile time error is necessary so future updates to packages don’t change functionality unexpectedly.
My motivation for this feature is to mitigate limitations on dynamic types. Consider the typical approach to handling dynamic json.
Access could be simpler through a hypothetical jnode package.
It’s possible that these “extension methods” could become part of the new object’s method set. This could lead to a kind of simple adapter pattern like so (excuse my bad example, I was trying to use the built in types so everyone could relate, though this would be much more useful in real applications)
Because this is just implemented through code rewriting, I believe there would be divergence in a variable's “compile time type” and its “runtime type”. This could lead to complexity in the compiler and complex interactions I haven’t thought of.
Fortunately, there’s no need to rush into this. We could start by saying extension methods do not contribute to the method set, and therefore do not help fulfill interfaces. Unfortunately, that caveat would fall to programmers to understand, raising the bar for learning go. I believe the ultimate design here would definitely be to allow the extension methods into the type’s method set.
The adapter pattern becomes even more powerful if we can find a way to disambiguate how extension methods would override a struct’s “real” methods.
One commonly proposed language change is to support chaining function calls. Consider this code taken from issue #56242
Interface methods could solve this issue like so
The compiler would be responsible for changing this code to
This example illustrates how the code rewriting can lead to a kind of support for generic methods.
Theoretically, this could be a starting point for how generic methods might be possible, though there’s a lot of investigation to happen before that’s possible.
One area I’m uncertain about is composition of interfaces. This case seems simple to support.
However, if we redefine our interfaces to include a collision, we have to deal with the ambiguity.
For now, this “mix” interface could give a compile error, either because you can’t compose interfaces with methods, or specifically because of the conflict.
A possible workaround is to adapt one interface. This would mean interface methods are not commutative through multiple compositions.
Alternatively, the extension methods could never commute through composition and a developer must always be explicit in the combining. Within interface extension methods, the composing pieces would become accessible, much like a composed struct.
What would change in the language spec?
to-do
Is this change backward compatible?
Because defining methods on interface receivers has been illegal, I believe this is a backward compatible change. No existing code will be doing this.
What is the cost of this proposal? (Every language change has a cost).
There are many costs, not least of all to the complexity of the compiler, especially if we are to include method set extension.
Some tools I know about would need updating: gpls, gofmt, golangci-lint, probably many others.
I don’t believe there is any runtime cost, besides, possibly, more function calls because of the structural changes in programs this would precipitate. I expect most of that cost will be regained through existing compiler optimizations.
Can you describe a possible implementation?
The simplest implementation I can imagine is just code rewriting. If we expand the scope to include method set extension, I believe this will require the concept of “compile types” which will be different than runtime types.
Orthogonality: how does this change interact or overlap with existing features?
I think this syntax synergizes very well with existing features. The syntax is familiar and does not introduce new tokens. With set extension, it also behaves quite naturally from a developer's perspective.
Does this affect error handling?
I don’t believe so, though it does facilitate more functional ideas, which might lead to a shift in how errors are treated.
Is this about generics?
Not specifically, though it does incorporate them.
Thanks very much for the feedback, Axel. I didn't think it was harsh, this is just the kind of information I needed before burning needless effort on the idea.Getting started with these conversations is quite intimidating because there is so much information and history, it's hard to find what is relevant, let alone digest it all. I appreciate that anyone spends the time to help a newbie.Just for interest's sake, I wanted to respond to some of your questions. I take your point about some sections of the proposal being incomplete or too sparse, but I don't think there's any point in spending time on that when there are obvious problems.> I also think one thing we should be clear on, is that methods fundamentally are never "just syntactic sugar for functions" - unless you really are proposing Uniform Function Call Syntax.I don't understand this, but I do take your word for it. This is probably the "something very obvious" I missed.
> In particular, your proposal has name spacing implications - it would be possible to have two methods of the same name on different interface types, while it is not possible to have two *functions* of the same name, even if they accept different interfaces as their first argument.I believe I accounted for this in the restrictions and scratched the surface of this challenge in the section about composition. However, my approach is obviously naive, I expect there's a good name for this problem and it likely has to be dealt with more systematically.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/72cacb9e-117c-4b5f-8015-1fdd7756f040n%40googlegroups.com.