Closed gopherbot closed 9 years ago
It could be argued that the compiler is wrong; or that both are wrong. IMO the spec/compiler should either: disallow lower-case (private) methods in interfaces (preventing the exploitation of this accidental behaviour), or else introduce a defined semantics for such methods, which allows implementation of private methods (in the Outer interface package). I do not think the present behaviour is desirable, or very useful.
While the spec may be unclear and/or misleading in this case, the implemented semantics is well established and understood. Any change affecting the implemented semantics is tantamount to a language change which might break existing code (these are not corner cases, it is a feature and people rely on the fact that they can define interfaces that cannot be implemented external to a package). But I agree that the spec should be clearer.
I'm not sure it is fair to call this particular behaviour of the code a feature. Can we at least agree to admit that it's a bit of a hack made possible only by the unplanned implications of earlier decisions? In fact the feature might do more harm than good: in what circumstances should the provider of an interface be able to prevent the user from using mocked or stubbed implementations in their testing?
| the implemented semantics is well established and understood. I'm not sure how it would be understood. It isn't in the spec, nor in the Go Language book 'Programming in Go' (2012), so perhaps people didn't stumble upon this before then. It can only be understood by experimentation; followed by dreaming up a possible application. Which, by the way, doesn't sound too useful to me. If it were so useful, it should have a clear marker in the source code, and therefore in the language. As it stands you have to be aware of this glitch, recognise it in the source and then reason as to why it is there. A 'private' keyword or some way of indicating restricted implementation types would be better. | Any change affecting the implemented semantics is tantamount to a language change which might break | existing code Yes. But then, how would this ever be false? There can be no code that relies upon behaviour that differs from the 'implemented semantics', can there? I think this is a general argument that is essentially: "It does what it does, and regardless of the language specification or design, we can never change what it does." This is of course not the case. Otherwise bugs would not be fixed! (Who says they are bugs, by the way--the spec? Nah!) I think we should at least debate this, rather than dismiss it as 'working as implemented'.
| The feature of using a non-exported method in an interface, which ensures that all implementations of the | interface are within the same package, is by design, is well-understood, is used by standard Go packages. | It is not an accident. I thought this was something that surprised Robert. | (Robert Griesemer is an employee of Google and one of the initial designers of the Go programming | language.) - Wikipedia Presumably this was a bit of the design that got past him. Possibly he only read the spec. I'm sure it is well understood, by those close to the language implementation, or implementing standard Go packages. I can see it might be useful for built-in or critical system/language packages, but for normal development, it impedes testability. I cannot stub the use of an interface in another component/package if it has a restricted implementation. Which then makes it hard for me to see how a tool like mockgen can cope with it -- unless it finds a way round it. (One which is well understood, designed in, and undocumented, no doubt.)
> I thought this was something that surprised Robert. Robert can speak for himself but I believe you have misread his comment. > I can see it might be useful for built-in or critical system/language packages, but for normal development, it impedes testability. Tests are normally in the same package and can therefore use the methods. In any case, if you find that private methods are a hindrance, don't use them.
In http://www.google.com/url?q=http%3A%2F%2Fwww.onebigfluke.com%2F2014%2F04%2Fgos-power-is-in-emergent-behavior.html&sa=D&sntz=1&usg=AFQjCNGPJvFRYNQdzvUgsJ6I8YkmLEJ4Rg Brett Slatkin suggests that Robert was surprised. | Tests are normally in the same package and can therefore use the methods. I think you misunderstand me: if a user of the package wants to be tested, it cannot stub an interface which has a private method. The tests impeded are for code that *uses* the constrained interface, not the implementation of the constrained interface. This also implies that the advice 'not to use it' is unhelpful, too. If others have used it already I cannot easily test my code that calls them without invoking them. And I cannot modify their packages. I am forced to wrap the interface with another and delegate, or some such unnatural act. Hence my reference to mockgen.
Just to make myself absolutely clear: 1) This is a well-understood language feature and not an accident, as iant already emphasized again. 1) No, I was not surprised by it; I only said that the spec should be clearer. There are a lot of language mechanisms where we (the designers and implementers) have a very clear notion and understanding, but where we didn't do a good job of writing it down clearly. This is why we are still refining the spec. Except for pathological or academic corner cases which are irrelevant in practice but matter for an implementation, we do not make changes to the language. This is _not_ a pathological case, and we are not going to change it. I will try to make the spec clearer, though. 2) In case there's any doubt: While I didn't write the compiler, I did write go/types which requires complete understanding of the subtle issues of interfaces, exports and non-exported names, specifically with respect to struct fields and method names. So, I can assure you that I am very familiar with the issues. In fact, go/types was written with the understanding of the spec in mind and exposed several (corner-case) bugs in the compilers. Please let's stop this discussion. Your point (spec is unclear) is taken, and I will fix it. Thanks.
CL https://golang.org/cl/149010043 mentions this issue.
AFAICT the revision to the spec does not clarify the issue or fix this bug in the spec. Non-exported methods of embedded interfaces are *not* treated the same as exported methods: the compiler refuses to allow you to implement them for the outer interface: it associates the original interface package with the non-exported method, which means you cannot implement the outer interface. At the very least the spec should say this, but I still consider this to be a language bug (or anomaly, if you wish).
AFAICT the revision to the spec does not clarify the issue or fix this "bug" in the spec. Non-exported methods of embedded interfaces are *not* treated the same as exported methods: although I can implement the outer (embedding) interface, the resulting implementation is not type-compatible with the inner interface. It would be, if the Exported and non-Exported methods were treated the same way. I think the spec should say something about type compatibility in this case, but no mention is made of this. I still consider this to be a language bug (or anomaly, if you wish) because it makes stubbing or mocking interfaces that exploit the restriction impossible: one cannot create a type-compatible implementation of an interface with a non-exported method outside of the implementing package.
From http://golang.org/ref/spec#Uniqueness_of_identifiers , two non-exported identifiers in different packages are considered to be different even if they are spelled the same. > one cannot create a type-compatible implementation of an interface with > a non-exported method outside of the implementing package. Yes. That is how the language is supposed to behave.
> From http://golang.org/ref/spec#Uniqueness_of_identifiers , two non-exported identifiers in different > packages are considered to be different even if they are spelled the same. Yes, although I wonder what is meant by 'identifier' after reading this. [Aren't every pair of identifiers from distinct packages considered to be different? How would they not be?] > That is how the language is supposed to behave. OK. I think it is silly (because of a mocking requirement and what possible benefit is it?), but OK: it is supposed to behave like that. I can accept that. This issue was accepted as requiring a 'clarification' in the spec for this behaviour, because the spec doesn't say anything about it. The revision that closed this issue makes no reference at all to this behaviour. I think the spec should say how the language is *supposed to behave*. Don't you?
When it comes to equality of identifiers, the section Ian mentioned (uniqueness of identifiers) _defines_ when two identifiers are the same or not. And that's what it comes down to for matching interfaces with implementations (and other interfaces). The spec has been corrected to not use the word "enumerate" to explain embedding anymore, because that was clearly misleading as you have pointed out. It now uses the word "add" (of methods) which is exactly what is meant, and how embedding of interfaces is implemented: The methods of embedded interfaces are simply added to the embedding interface (unless there's a conflict in which case an error is reported - and that's covered by the requirement that interface methods must be unqiue). The spec also mentions now that all _exported_and_imported_ methods of an embedded interface are added, exactly to make this behavior of embedding extra clear. The fact that one cannot necessarily implement such an interface is a consequence of the uniqueness requirement, and it's an intended and welcome side effect (and Ian has pointed that out already as well). It permits the definition of interfaces that can only implemented by using objects of the package that defines that interface. I'm considering this issue closed at this point.
by Zteve.Powell: