I'm not sure I understand the need for contracts really. If a function with type argument is compiled, the caller should use specific types to call it, so the compiler can determine whether said type has the required methods used in the generic function. Therefore the compiler knows everything it needs to fail with a compile-time error without any extra ugly "contract" syntax. What am I missing? Are some types only resolved at runtime?
So the lessons from a number of PL implementations show that this kind of information needs to be explicitly encoded in codes, and that's the whole point of having "contract", "type class", "concept" or whatever else it's called. Compilers may be able to infer technical details of type, but at least for now it's not sophisticated enough to infer the original intention of the writer.
Since Go doesn't have standalone object files / binary libraries, it's not going to produce confusing link-time errors for this.
template <typename T>
void foo(T bar)
{
bar.hardToSpell(); //1. Error here?
}
struct Bar() {
void ahrdotspell(); //2. Error here?
}
Bar buzz;
foo(buzz); //3. Error here?
In this particular case it's easy to deduce that something is misspelled and error 2 is the real error but the compiler is probably going to tell you it is error 1. And once you get into more complex programs the problem might be that the Bar class is not simply supported by the generic function or that the generic class is implemented incorrectly.If you instead could add an interface constraint on T (like for example C#/Java/Typescript allows) it would be clear from the contract that this interface defines all the required functions on T if Bar doesn't implement those it is unambiguous that the implementation of the interface is not there.
Then you just have a 4th possible spot for an error. I don‘t see an essential difference between considering the function definition as the reference and having a contract (where typos are possible as well), just the latter being cumbersome and superfluous.
So each of the 3 potential errors can always be pinpointed exactly, as opposed to my example where it could be any 3 (or all 3 simultaneously) of them because "T having a hardToSpell-method" is not part of any public interface description, it's something deep in the implementation of the foo-function (in my example it's not very deep but in real code it usually is).
To iterate my point, it serves as a "contract" between users and the code author. Without this, any user of a generic function will implicitly depend on its implementation, which creates tight coupling. Cryptic error messages or unintentional breakage are just undesired side effects from not having this clear boundary. So, the interface of generic functions needs to be explained to its users anyway; then why not making this understandable to compiler and prevent users from making such mistakes?
I agree the introduction of generics is a big worry not because of what good programmers will do with it, but because of what people more enamoured with pretty abstractions than code that does work will do with it. I don’t particularly want to live inside someone else’s creation for weeks just to understand it. The existing culture of no nonsense and minimal abstraction should help police that though.
I’m not that keen on the syntax of contracts either, but we’ll see what comes out at the end, that’s not so important - the idea of contracts looks like a good one in theory at least, but it feels like they could have used interfaces instead somehow (I’m probably missing something there).
The errors proposal is interesting as it looks like try/catch at first glance but importantly doesn’t leak that error across function/lib boundaries as exceptions do, so it’s a natural extension of the existing approach.