Hi Stanislav,
s2gatev <
ma...@s2gatev.com> writes:
> Hey Axel,
>
> Thanks for getting in touch!
>
> I am doing basic compilation compatibility verification. This means that a
> newer version of the interface is compatible with the older if it doesn't
> break compilation of code that uses it. Here are the rules I based the
> compatibility checker on:
>
> 1. Packages should have the same names in the newer version.
That is obviously a given :)
> 2. Package should export vars and consts of the same names and types in
> the newer version. A var could become a const and vice versa, this won't
> break compatibility.
A reverse dependency could assign to a var, so making a var into a const
might break compilation.
A const might be used in a context where a var is disallowed (e.g. in an
array bound) so turning a const into a var might also break compilation.
> 3. Packages should export functions and methods with the same names,
> receiver type, parameter types and result types. A parameter type could
> become var arg (e.g. "int" -> "...int"), this won't break
> compatibility.
It might prevent a type from implementing an interface that it
implemented befor. Or someone might store a func in a variable of a
declared type. Both of these would then break, when you change an arg
into a var arg.
> 4. Packages should export types with the same name and definition.
> Structs should export fields with the same names and types. Interfaces
> should export methods with the same parameters and result types.
Unless that's a "subset", instead of an "equal", this seems reasonable
:)
> 5. I will be experimenting with some interface rules that allow you to
> "bump" a type to a less-strick interface type and this shouldn't break
> compatibility.
I'm unsure what you mean here. If you mean, for example, to add a method
to an interface, thus making it less strict (which is one way to read
that, maybe), this could also break compilation, because a reverse
dependency created an implementation of that interface and passed that
and suddenly the implementation doesn't satisfy it anymore. In the other
direction (i.e. removing a method, thus making the requirements on the
implementor less strict, which could be another way to read it), a
reverse dependency might use the type and try to call the removed method
and then gets broken. Either way, I don't see how any change to an
interface type can be guaranteed to not break any reverse dependency.
> If I understand you correctly you are thinking of deeper (than compilation)
> compatibility semantics.
No, not at all :) That's after all the halting problem and as such
theoretically impossible to solve.
To explain my position: I've been of the opinion (for a while now) that
*every* API change might break compilation in a strongly typed language
such as go. This would then mean, as a corollary, that a strict "we can
never break compilation" doesn't give a useful definition of
compatibility, as every change would then be a breaking change. So, to
get to a useful definition, you must allow *some* potential compilation
breakages -- and to be clear, that is absolutely fine, I can get behind
the quantitative difference between removing a type, which will likely
break something and adding a field to a struct, which will possibly, but
unlikely break something. But you must split compilation breakages into
two sets "probably breaks something" and "probably doesn't break
something", instead of "possibly breaks something" and "guaranteed to
not break anything".
My opinion is that currently the go community is lacking a consistent
agreement what these "probably non-breaking" changes are, that need to
be allowed. The go compatibility guarantee makes a start here by
excluding dot-imports, which then allows at least addition of new
Identifiers in the set of "probably non-breaking". I believe a tool such
as yours (or the go api checker that is used on the core repo) could go
a long way to *create* such a specific agreement which is
computationally checkable. Which is why I'm interested
in having a good documentation of what it's specific mapping of changes
into "probably breaks" and "probably doesn't break" is.
In particular, please don't understand my rattling of your particular checks
as devaluing your tool or discouraging further work on it :) I'm happy
to help as good as I can in enumerating the potential situations in
which a change could be breaking to assess the likelihood of breakages.
Best,
Axel