A Go contract design combining the strong points of official draft v1 and v2
github.comThat’s a lot of new, effectively, keywords that you’re introducing. I don’t know if that’s a bad or a good thing, but I suspect the community will have a hard time with it. Otherwise this is a creative solution.
It uses effectively only the one keyword assure. The constraints look like fields on the type variables, which have an ambiguity with package private method expressions with the same name.
The constraint expressions are expected to be used in generic declaration body scopes instead of top package scopes. For example: https://github.com/dotaheor/unify-Go-builtin-and-custom-gene..., so I think the ambiguity should not happen.
I would prefer naming that feels less procedural. I think of it like a guard so maybe `when` or `where`. It can also just be some punctuation like `| cond` `| another-cond`.
I would love to see something along these lines in Go. It would probably not end the abject negative reactions people have to the language as I suspect those are not actually about what it lacks, but it would solve some practical problems.
Metaprogramming is the root of much complexity in programming, but the main alternative is reflection. I suspect that with sufficiently advanced metaprogramming reflection is essentially never needed, but you end up with other costs.
In Rust you might find yourself deriving nearly every struct with a laundry list of traits. This is a cool feature, but I think the equivalent situation in Go is worth considering. Go doesn't have traits or macros, but it does have reflection. In effect, in Rust you can choose what is generated off of your structs, whereas in Go you get a baseline amount of generated information and you choose what to do with it at runtime.
Despite being undoubtably less efficient at runtime, it has its advantages. You don’t have to think about deriving Debug, for example, because there’s enough reflection information to do the needful at runtime instead. In Rust you may pay more executable space per struct in some cases due to having to generate lots of bits of code, whereas in Go you end up with a lot of little bits because each struct generates some data.
The important thing to think about here is that these approaches are actually not so different. They shift costs around differently, but they ultimately accomplish similar things. I think inherently the Rust approach feels much more powerful, and I think that’s a fair assessment, but the Go approach is flexible. The same struct metadata can print debug information, and not only that, it can do so in multiple formats. It can be used to serialize and deserialize data like JSON. I maintain a small library called Restruct that implements binary serialization a la Kaitai Struct that uses reflection.
Not having generics has been a huge advantage and disadvantage of Go from the beginning. Generics really do carry a lot of implications. On a sliding scale of generating interface implementations from structural information at compiletime to purely reflection based strategies, I think there is a sweet spot.
My knee jerk reaction to contracts in Go was that it sucked. It seemed like a very weak implementation of metaprogramming. However, I’ve come around to thinking it might be the way to go.
Having advanced templates would be useful too, but they would have drastic implications as well. What I like about contracts is they feel self contained. It feels like everything else around the contracts usages is fairly normal and it won’t have a strong impact on how Go is written today.
So while it won’t make a lot of people happy, it will probably help the day-to-day Go programmer a bit, by providing the ability, finally, to generalize functions. I’d argue there are rarely many circumstances where this is really needed in day to day Go programming, and therefore this fairly minimal approach shall serve the language well.
I think the future of Go and how it fits in with other modern programming languages will be firmly cemented by which approaches are chosen to solve common Go problems in Go 2 and 3, and this is certainly a big one. If you feel unsatisfied by this, I think there is still room for more new programming language research, and highly recommend people try languages like Zig, Nim and of course Rust. Go may not be your thing or maybe not suitable for your use cases, and I suspect its possible it never will be suitable for all use cases after all. The same can be said about all programming languages for the foreseeable future.
Regarding this particular draft, my only real input is that I dislike the assure syntax for some reason. It would also be great to avoid new keywords if possible, though I have no idea how you could avoid that here.
I’m a C programmer, and I don’t really see what adding this additional complexity will really bring to the language. I always think of C as half typed. I get all the benefits of static typing where it makes sense, but I’m also not afraid to do some casts and throw around some void pointers. Go has it so much better with the underlying type attached to the interface.