Or structural type systems for the pendantic, but I think most people know what I mean when I say “compile-time duck typing”.
For one reason or another I’ve read quite a few blog posts about how great the Go programming language is recently. A common refrain is that Go’s interfaces are amazing because you don’t have to declare that a type has to satisfy an interface; it just does if its structure matches (hence structural typing). I’m not sold on how great this actually is – more on that later.
What I don’t understand is how this is presented as novel and never done before. I present to you a language from 1990:
template <typename T> void fun(const T& animal) { cout << "It says: " << animal.say() << endl; } struct Dog { std::string say() const { return "woof"; } }; struct Cat { std::string say() const { return "meow"; } }; int main() { fun(Dog()); fun(Cat()); }
Most people would recognise that as being C++. If you didn’t, well… it’s C++. I stayed away from post-C++11 on purpose (i.e. Dog{}
instead of Dog()
). Look ma, compile-time duck typing in the 1990s! Who’d’ve thunk it?
Is it nicer in Go? In my opinion, yes. Defining an interface and saying a function only takes objects that conform to that interface is a good thing, and a lot better than the situation in C++ (even with std::enable_if
and std::void_t
). But it’s easy enough to do that in D (template contraints), Haskell (typeclasses), and Rust (traits), to name the languages that do something similar that I’m more familiar with.
But in D and C++, there’s currently no way to state that your type satisfies what you need it to due to an algorithm function requiring it (such as having a member function called “say” in the silly example above) and get compiler errors telling you why it didn’t satisfy it (such as mispelling “say” as “sey”). C++, at some point in the future, will get concepts exactly to alleviate this. In D, I wrote a library to do it. Traits and typeclasses are definitely better, but in my point of view it’s good to be able to state that a type does indeed “look like” what it needs to do to be used by certain functions. At least in D you can say static assert(isAnimal!MyType);
– you just don’t know why that assertion fails when it does. I guess in C++17 one could do something similar using std::void_t
. Is there an equivalent for Go? I hope a gopher enlightens me.
All in all I don’t get why this gets touted as something only Go has. It’s a similar story to “you can link statically”. I can do that in other languages as well. Even ones from the 90s.