Why I like Golang's interfaces
medium.com
medium.com
Especially when you have features like union and intersection types (which Go doesn't have), you can really mold the structure into whatever you need (i.e. to fit whatever data model you're working with), and be very productive without having to come up with weird inheritance hierarchies "just because".
It'd be nice to have the option to explicitly enforce nominal typing in specific places (maybe with its own keyword), but for most cases, structural typing seems good enough so far.
func myrw(rw interface { io.Reader; io.Writer }) {
rw.Read(nil)
rw.Write(nil)
}
https://play.golang.org/p/D-lJ1pL3MZOf course, Go compiler itself uses "unions" with runtime-tricks to implement them. But we're talking about runtime then, not compile time type safety and correctness.
In both case, as opposed to Go interfaces, they are actually safe (downcasting is forbidden, upcasting is checked at compile time) and the OCaml language has very good type inference, type annotations are not needed.
Is this what you mean? Trying to pass a []fooint as a []Fooer doesn't work: https://play.golang.org/p/dmFfr0Exnk - that doesn't work in many languages, which is sad, but is for reasons that make sense if I think about what it would actually be doing under the covers: an interface value needs to store type information and/or a pointer to some kind of function pointer table, so you'd have to recreate that for each list element if you change the type of the list.
func workOnInterfaceList(foos []Fooer) {...}
func workOnStructList(foos []Foo) {
workOnInterfaceList(foos) //compile error: cannot cast []Foo into []Fooer
}
It's obvious how to resolve this: func workOnStructList(foos []Foo) {
foos2 := make([]Fooer, len(foos)
for idx, foo := range foos {
foos2[idx] = foo //casts Foo into Fooer implicitly
}
workOnInterfaceList(foos2) //ok now
}
Why can the compiler not insert this automatically (esp. given that [] is a builtin type)?But then again, Go has a reputation of making you write the same code over and over again, so I'm not surprised. (And I say that as an empathic supporter of Go.)
As an aside, if you think this example is artifical, here's it happening in real-world code: https://github.com/majewsky/sqlproxy/blob/f5b297e7dce14c0453...
The reason that []Foo is not the same as []Fooer is simple: they are different data types. They have a different layout in memory.
Internally, an interface value is a pair of pointers. One points to the concrete value, and the other points to the concrete type of the value.
The compiler needs to know the size of the things it's working with in the case of slices, because it needs to know how to compute offsets for indexing and iteration. It could make an implicit copy at runtime, but that could trigger a costly operation that would be invisible to the user. Go wants expensive operations to be visible, so you have to make the copy yourself.
Just curious: did you mean empathic or empathetic or emphatic? :-)
it does when the language has generics. You in fact wouldn't need 2 types []fooint and []Fooer but a single List<T Fooer> , as long as fooint implements Fooer.
So with Go you end up writing functions that convert types manually. The 1000's time you do this, it starts getting a bit tiresome.
I agree that having generics in Go would be fantastic, and I'm excited at rsc's articulation of why. But they don't completely solve this problem.
The worst I can think of are possible developer confusion, and maybe your autocomplete suggesting the wrong thing.