12 ms·
Is Go Duck-Typed?
- mbell 7y agoThis is called Structural Typing[0] and is in contrast to Nominal Typing[1] (e.g. Java). 0: https://en.wikipedia.org/wiki/Structural_type_system https://en.wikipedia.org/wiki/Structural_type_system 1: https://en.wikipedia.org/wiki/Nominal_type_system https://en.wikipedia.org/wiki/Nominal_type_system
- twistedpairs 7y agoIs it just me or is the wikipedia article for duck typing really bad? The example they have is not illustrative or explanatory at all. class Duck: def fly(self): print("Duck flying") class Sparrow: def fly(self): print("Sparrow flying") class Whale: def swim(self): print("Whale swimming") for animal in Duck(), Sparrow(), Whale(): animal.fly() output: Duck flying Sparrow flying AttributeError: 'Whale' object has no attribute 'fly' This moreso shows a dynamic typing issue.
- jolux 7y agoDuck typing really only makes sense in the context of dynamic typing.
- twistedpairs 7y agoThat's a given. That comment doesn't further the conversation in any way though?
- jolux 7y agoYou said it was a dynamic typing issue, not a duck typing one, when in fact the two are inseparable in the example you gave. That is absolutely an example of duck typing, you’re calling the same method on three different types with no subtyping relationship with a single invocation.
- jolux 7y agoOther notable examples include the OCaml object system which implements subclasses using row types (structural subtyping), meaning that subclasses are not technically subtypes, and TypeScript interfaces, which are also structurally typed.
- momentoftop 7y agoAnd it inherited its module system from Standard ML, where module types (signatures) obey structural subtyping: a module can be typed to any signature it includes.
- the_unproven 7y agoI just recently found out about these two types of system. It's strange how people (like shown in the article) don't emphasize(know) it when talking about types in languages. Interestingly, python has included structural subtyping in 3.8[1] as part of the typing module. [1] https://www.python.org/dev/peps/pep-0544/ https://www.python.org/dev/peps/pep-0544/
- bjz_ 7y agoIt's probably more that most mainstream typed languages, like C, C++, Java, C#, etc. have mainly gone down the nominal route, as opposed to languages like Standard ML and Typescript that tend to be more structural. Structural vs. nominal typing is pretty well known in programming language design circles, but circulating that knowledge is a constant uphill challenge. One of the reasons many of us are frustrated with Go's designers were how quickly they threw this work under the bus as part of their initial marketing strategy, which kind of knocks away the ladder for anyone else who is curious to learn about this stuff.
- masklinn 7y ago> languages like Standard ML and Typescript that tend to be more structural. AFAIK SML is nominal. OCaml has a structural subsystem in that its object system is structural. The vast majority of the language is nominally typed. > It's probably more that most mainstream typed languages, like C, C++, Java, C#, etc. have mainly gone down the nominal route It's not just mainstream typed languages. Almost all statically typed languages use nominative typing. Tt doesn't have any real drawback and it's just simpler to understand and work with. A language like typescript would go with a structural system because it's a much easier bridge and sell from an object-oriented dynamically typed world.
- bjz_ 7y ago> AFAIK SML is nominal I guess I was referring to how records are structural. Although granted tagged unions are nominal, and you can get nominal typing through modules. I was probably wrong in posing it as 'one or the other' - seeing as many languages have a mix of both. I'm definitely not saying that nominal typing is bad, it's just that it's nice to have the option to go structural if you want, and many have not known that this option exists.
- loopz 7y agoDuck typing is according to Wikipedia: https://en.wikipedia.org/wiki/Duck_typing https://en.wikipedia.org/wiki/Duck_typing Duck typing as a concept was re(?)-popularized around the advent of ruby, so might make sense to talk from a ruby perspective: http://rubylearning.com/satishtalim/duck_typing.html http://rubylearning.com/satishtalim/duck_typing.html With ruby, most everything you interact with is a true object, ie. you can modify behaviour of almost all classes/objects by adding/removing methods, mixin, etc. It's not unheard of monkey patching Integer or Array classes, core parts of standard library and language. Thus, you could dynamically invoke any object with any methods, without regards to compile-time constraints or "types". Errorhandling thus delegated almost entirely to runtime beyond basic syntax parsing. Golang furthers the notion of capabilities as shared method signatures, by loosely binding this into interfaces, and enforcing many errors by static type checks at compile time. It's interesting as a development away from inheritance and towards composition and loose coupling of static program components.
- tgv 7y agoWouldn't that make any language with reflection Duck-typed? That would suggest Wikipedia's definition is not very useful.
- loopz 7y agoFair question! "Duck typing" can be implicit, if you can call quack() on this() without error, you (the programmer) assume it's a Duck. The language may make it explicit (loosely structural typed like in golang, or other variations), or implicit (like in ruby). There are varying degrees, but you should be able to have a collection of the "Ducks" and be able to invoke quack() on them all. Can't really say I see the connection with reflection. If quack() fails, you can get an error, either at runtime or compile-time. So no reflection required, though using reflection you may avoid errors dynamically. Structural Typing as a concept is much better defined though.
- abhorrence 7y agoReflection isn’t required, but with sufficient reflection a language could essentially be extended to include duck typing.
- tedunangst 7y agoThere was also a fork of java called white oak that added structural typing.
- pansa2 7y agoI think the difference between structural typing and duck typing is one of explicitness. When passing an object `o` into a function `f`, structural typing enables `f` to explicitly say “`o` must support `a`, `b` and `c`”, even if `f` only ever calls `a` and `b`. With duck typing, the interface is implicit - if `f` only calls `a` and `b`, then that’s exactly what `o` needs to support. Duck typing also allows more dynamic interfaces, like “`o` must support `a`, and if calling `a` returns true, it must also support `b`”.
- int_19h 7y agoOCaml has structural typing with full type inference: # let f obj = obj#foo 1 2;; val f : < foo : int -> int -> 'a; .. > -> 'a = <fun> but it's generally not called duck typing. I think the more dynamic nature is really the crucial difference. In OCaml, if the function above had an if/else, and called #foo in one branch, and #bar in the other, the type of the object would be inferred as having both methods, and would enforce that at compile time. With duck typing, you could pass something that only has #foo, so long as the right branch is taken.
- codebje 7y agoWhy not just have "a" return something containing "b"? In Python 3.8 for the brief syntax afforded by assignment expressions: def foo(o): if (b := o.a()) is not None: b() Because the original interface indicates that all "o"s must know about the link between "a" and "b", there's no loss of generality, but now there's a statically inferable structure: "o" must support an "a" that returns None or a callable. If you do a tiny modification to "def foo(o, a): ..." then this doesn't apply any more, and you're outside the realm of structural types and into the realm of dependent types, which means you must drink deeply of the static typing kool-aid and want to write type signatures that run the risk of being more complex than the code they describe. (But I like to quote Conor McBride: "if you're not going to write types your type system has to be stupid enough a computer can do it", from https://www.youtube.com/watch?v=3U3lV5VPmOU&feature=youtu.be&t=2369 https://www.youtube.com/watch?v=3U3lV5VPmOU&feature=youtu.be...).
- _old_dude_ 7y agoJava type system is not fully nominal anymore, the operator :: does structural typing. interface I { void m(); } class A { void hello() { ... } } I i = new A()::hello;
- jolux 7y agoIsn't this just the stuff making all lambdas implicitly implement a single-method interface and such?
- int_19h 7y agoOn JVM level, yes. But on Java level, the type of the method is structurally matched to the type of the interface.
- jolux 7y agoThe name of the function is typically the same though, even with structural typing. This doesn’t seem quite like that.
- int_19h 7y agoIf you mean the name of the function in the interface, it's basically ignored in this pattern, similar to how argument names are ignored. The entire interface definition should be treated as a function type with some redundant metadata.
- _old_dude_ 7y agoyes, a lambda is (mostly) desugared as a static method and then the operator :: is applied.
- LessDmesg 7y agoThe difference is small, and the fact remains: structural typing is utter crap, and so is Go.
- skrebbel 7y agoI just want to add TypeScript to the list of structurally typed languages. It works delightfully well in TypeScript (arguably better than it does in Go). I love how you can just return a custom object literal from a function and TypeScript figures out the type, without ever giving it a name. This lets you explore with pretty much the same flexibility and speed as with dynamically typed languages, and only after you shaped your code sufficiently well, you can decide whether you want to name some of the types and make everything a bit more "solid".
- smoyer 7y agoBetteridge's law of headlines would say "no" (as would @mbell in this conversation). Curious, the author says "undefined". @mbell FTW!
- nemith 7y agoI like to call it Quack typing as interfaces are based on behavior and not attributes.
- throwaway894345 7y agoStructural subtyping can be based on attributes a la Python’s Protocols.
- IshKebab 7y agoNo they aren't, they're matched based on method names. Go isn't duck typed, except for interfaces which are. Pretty simple.
- jolux 7y agoNo, it is not duck typed. It is structurally typed, as @mbell explains.
- zwieback 7y agoI like the histogram showing how much more frequent interfaces with just one or two methods are. How frequent are problems where classes unintentionally use an interface due to identical signatures? Seems likely to happen in theory if so many interfaces have few functions.
- mjr00 7y agoIn theory, yeah... in practice, as someone who uses Go and Python with mypy (which supports structural type-checking using the Protocol interface) professionally, I haven't seen it happen. It's easy to think of contrived examples like the one in the post, where Cat/Dog/Table all implement GetLegCount(), but in practice your interface methods usually aren't that simple, and interface signatures like func InsertFoo(*sqlx.DB, Foo) (int64, error) are very unlikely to be unintentionally implemented.
- mrkeen 7y agoI tend to use nominal types a lot in other languages, so I have a lot of types with id() or value(). Would those be mixed up?
- masklinn 7y agoIf the interfaces have only id/value with matching parameters and return types then yes the interfaces would be functionally identical and all objects satisfying one of the interfaces would satisfy the others.
- morelisp 7y agoThey would be, but it's also unlikely you would be able to write something useful using only the id and value such that you might confuse something. There's some "self-limiting" - the smaller the interface, the more likely you get accidental implementation, but also the less likely you are to confuse them in a way that typechecks but actually does something unexpected. A practical example might be io.Closer - I am certain I implemented this in a dozen types I never intend to pass to any io functions. But no io functions actually take just a Closer, and if I did write some generic cleanup routine that only requires a Closer, it's probably fine and maybe even good if it works on byte IO and gRPC connections and Kafka streams. (Especially without generics, it might be the best I can do.)
- justincredible 7y agoThis is a poorly written article. It requires that one already understands the concept to even follow along, while providing no additional information nor conclusions.
- didibus 7y agoI think duck typing is different from structural typing, and go is not duck typed, but structurally typed. I think the difference is that in duck typing, there is no real type. A thing is a duck if I can use it as one, and that same thing can be a chair if I can also use it as one. So on duck typing, we say, does it have the properties I need, if so I can happily use it, don't care what it is. And that can only be done at runtime, since you can't fully predict the runtime properties the thing will have. In structural typing, we say that the type of a thing is defined by its structure. This thing is a Duck because it is of the expected structure of a duck, which might be to define a quack string -> string method. At this point, you say the thing is a Duck. You can do this at compile time, because you have defined statically a set of named things and their structure, and then you can track the things passed to you and match their structure to the list of structures you have to find the one that fits and consider it of that type.
- samatman 7y agoI agree, and the difference is easy to illustrate. quack() in duck typing, has to have no arguments and return a string (let's say). We can have a NotarizedDuck class where quack(true) instead returns the number of times the NotarizedDuck has quacked, so long as quack() itself returns a string, it will pass the unit test. In structural typing, it's illegal to call that quack, because it's the wrong function signature.
- iddan 7y agoI longed for an article as well written as this one.
- mkchoi212 7y agoI wonder how this post would change if Go releases real powerful generics with Go 2. If I remember right, the authors of Go are considering adding real generics - other than `interface` - due to users' complaints. Personally, don't think Go needs to change anymore and be "duck-typed" via more powerful generics but we'll how it goes :p
- flippinburgers 7y agoStructural Typing is being used, but unlike the wikipedia article being linked to by mbell, I don't think the distinction between duck typing and structural typing is that significant. It boils down to does the method parameter, which is an interface, describe the subset of the struct that you require or not? That is it. It is duck typing, but it simply requires explicit definitions (interfaces). To use a popular quote "if it acts like a duck-type it is a duck-type".
- kazinator 7y agoIf you have to declare any sort of is-a or is-a-like relationship to be a duck, then it's not duck typing. Reason being: without the declaration, you can quack all you want, but you are not substitutable for a duck. Duck typing means that the presence of suitable properties and behaviors alone determine substitutability, not the is-a relationship.