88 comments

[ 2.1 ms ] story [ 149 ms ] thread
This 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

1: https://en.wikipedia.org/wiki/Nominal_type_system

Other 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.
And 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.
I 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/

It'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.

> 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.

> 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.

Are records structural in Standard ML? What happens if you have multiple records with the same structure but different names? I thought structural typing of records was not very useful without row polymorphism.
Records of the same structure but different names are equivalent. In fact tuples are just sugar over records with numeric fields starting from 1. Note that this is not the same as in OCaml, afaik.
It depends. OCaml explicitly has polymorphic records as well.
Modules are structurally subtyped in SML. They are not nominally subtyped. You do give them names for readability and reuse, but the name is not important to the typechecking. Module types (signatures) are checked according to the types of the fields they have, with one module type being a subtype of another if it includes the other's fields. For instance, if I have a functor:

    functor F(S : sig val x : int end) = struct ... end 
then I can apply F to any module that includes a field x of type int. I don't even have to name the functor argument:

    structure Foo = F(struct val x = 3 val y = 4 end)
Unions, on the other hand, have nothing to do with subtyping. In the union

    datatype foo = Bar | Baz

    val foo1 = Bar
    val foo2 = Baz
There is no subtyping. There is exactly one type in question, which is "foo", and no other types to form a subtyping relationship.

You might be thinking instead about how some languages implement algebraic data types over a language like Java by replacing "foo" with a supertype and implementing the variants as subtypes. Scala and Kotlin are examples of this. Doing things this way allows you to do interesting things that are not possible in SML, such as typing for exactly the variant you expect.

Ocaml has always had, in addition to normal records, structurally typed records and polymorphic variants. It also has SML's structurally typed module system, though goes much further by allowing modules as runtime values.

This is an amazing observation for someone unfamiliar with structural typing. It's probably also why python programmers tend to enjoy Go.
Duck typing is according to Wikipedia: 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

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.

Wouldn't that make any language with reflection Duck-typed? That would suggest Wikipedia's definition is not very useful.
Fair 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.

Reflection isn’t required, but with sufficient reflection a language could essentially be extended to include duck typing.
No more than any language with casts is untyped. Reflection lets you avoid the type system and program in a duck-typed way, but unless the language uses reflection for normal values and functions then I wouldn't call the language duck typed as a whole.
I'm not sure what you mean by 'not exact' since the article you posted explictly contrasts duck typing with structural type systems of which Go is given as an example.
(comment deleted)
The contrast in the Wikipedia article is:

> Structural typing is a static typing system that determines type compatibility and equivalence by a type's structure, whereas duck typing is dynamic and determines type compatibility by only that part of a type's structure that is accessed during run time.

That sounds like static vs. dynamic implementations of the same thing.

When you're talking about type systems, "dynamic" really doesn't make much sense to talk about. It's not like the runtime constructs a typing model out of its observations of the properties of objects' runtime interactions. The notion of there being a "type" to a duck-typed object is purely a theoretical one; in practice, the object just does whatever it wants moment-to-moment, and the runtime has no idea.

Consider a generic "proxy" object that can be arbitrarily disconnected and reconnected to objects of different types. These can exist in any duck-typed runtime system. What is such an object's "type", even in the sense of its "runtime type"? There isn't one. There might be a sense in which it has an instantaneous type—a type it has as of a given program world-state—but that information is inaccessible to the runtime, since any probing it might do to ascertain this might also cause the object's instantaneous typing to change in the middle of the probing procedure.

(See also: the Universal Server in Erlang [ https://joearms.github.io/published/2013-11-21-My-favorite-e... ]. What is this server's "type"?)

A type system is something that can make guarantees about the behavior of a program in advance. In that sense, duck-typing isn't really a type system. It's just asking objects questions and then blindly trusting the responses you get. There's nothing but convention guaranteeing that an object that e.g. in Ruby implements `respond_to?` a certain way, will _actually_ respond to those methods when they're called. The object can lie. Which means you don't have a guarantee, and so you don't have a type system.

The fact that one is checked statically and the other is only enforced at runtime is a significant different between them, since the static approach will reject some programs that are dynamically safe. The term duck typing when employed by dynamic languages like Ruby is misleading since it's a consequence of not having a type system at all.
> 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.

This, I think, is the main point of the parent comment.

I think the distinction (and overlap) come into focus when you think computationally: how is a type computed? Type semantics are determined by the type checker, and Go's interface types are checked at compile time. Duck type checking happens at runtime. There's no language support in Go for this, but the closest thing would be passing a bunch of empty interfaces around and using the stdlib's reflect package everywhere so that you never panic, but sometimes return a user-generated TypeError.

Sure, Go's interfaces allow clients to accept multiple concrete types, but that's checked at compile time, not runtime, and so it will never be duck typed.

While Go interfaces are usually checked at compile time, this doesn't have to be the case - you don't even have to use reflection. Consider the following program.

    package main

    import "fmt"

    type Dog struct{}

    func (d Dog) NumberOfLegs() int {
        return 4
    }
    func (d Dog) Genus() string {
        return "Canin"
    }

    func main() {
        var dog interface{} = Dog{}
        fmt.Println(dog.(interface{ NumberOfLegs() int }).NumberOfLegs())
        fmt.Println(dog.(interface{ Genus() string }).Genus())
        fmt.Println(dog.(interface{ Happy() bool }).Happy())
    }

This fails at runtime, as `Dog` doesn't have `Happy` method.
Why do good people downvote what they don't understand?

https://play.golang.org/p/d0TkGTCZa2S

Runtime results in panic:

  4
  Canin
  panic: interface conversion: main.Dog is not interface { 
  Happy() bool }: missing method Happy
  goroutine 1 [running]: main.main() /tmp/sandbox251002434/prog.go:18 +0x200
Using interface casting, Golang do support "duck typing" in similar fashion as ruby's Object class. Here, by convention, the instance of Dog doesn't support "type" Happy(), therefore fails at runtime.

So golang is somewhat dynamic as well, although using interface{} is kludgy and one would want to avoid that and reflection whenever possible.

There was also a fork of java called white oak that added structural typing.
I 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`”.

OCaml 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.

Why 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...).

Java 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;
Isn't this just the stuff making all lambdas implicitly implement a single-method interface and such?
yes, a lambda is (mostly) desugared as a static method and then the operator :: is applied.
On JVM level, yes. But on Java level, the type of the method is structurally matched to the type of the interface.
The name of the function is typically the same though, even with structural typing. This doesn’t seem quite like that.
If 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.
The difference is small, and the fact remains: structural typing is utter crap, and so is Go.
I 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".

Is 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.
Duck typing really only makes sense in the context of dynamic typing.
That's a given. That comment doesn't further the conversation in any way though?
You 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.
Betteridge's law of headlines would say "no" (as would @mbell in this conversation). Curious, the author says "undefined". @mbell FTW!
I like to call it Quack typing as interfaces are based on behavior and not attributes.
Structural subtyping can be based on attributes a la Python’s Protocols.
No they aren't, they're matched based on method names.

Go isn't duck typed, except for interfaces which are. Pretty simple.

No, it is not duck typed. It is structurally typed, as @mbell explains.
I 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.

In 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.
I 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?

If 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.
They 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.)

(comment deleted)
In practice if your 'id() string' method returns an id as a string for the object doesn't it do what you need it to? So you actually need to care whether the intended to satisfy your contract or not?

You might care if you expect the Id string to look a particular way. But then you probably wanted to use a type other than string for the return type.

> you probably wanted to use a type other than string for the return type.

That's the point. In order for (String userId) to not be confused with (String password), I wrap them with nominal types UserId and Password.

If the language looks at both UserId and Password and decides they're interchangeable because they both satisfy the contract 'contain a String', then that completely negates the point.

And at that point, you have.. nominal types with less documentation?
I've been programming for over 20 years primarily in languages in which this can nominally happen, and I think I've literally never seen it happen. I've been in situations where I expected a method and it was missing (e.g., Python code expecting a vaguely-file-like object to have seek, receives a socket), but that's the closest to this sort of thing I think I can say I've come. I've never had a case where I called a method that I expected to be one sort of thing, and got a method that was actually a completely different thing.

I used to worry about this sort of thing, but now I think that any language designer making a new language should just consider it a non-issue. If you happen to make it impossible for some other reason, sure, cool, I guess, but don't put even a little bit of effort into worrying about this case.

(Note I'm not saying it can't happen, or even be bad if it did. I'm sure it's happened to some people, and of that set, I'm sure there must be at least one person with a story of how it went really badly wrong somehow. I'm just saying that compared to the sorts of issues that affect every programmer in your language every day, worrying about this edge case is not productive. If this is your biggest problem in your language, or even fits into the top 100, please let me know about your surprisingly perfect language as I am very interested in using it.)

My experience is the same.

It's interesting to me that people are very concerned about Go's structural subtyping, but they generally have no qualms about passing functions around. Given that the contract for a function is the function signature, and the contract for an interface is [all function signatures and their associated names], surely the latter is less error prone?

A function explicitly has no further semantics than its inputs and outputs. The receiving function shouldn't, and won't, assume anything about what the function it's passed does. Whereas when you're passed a bundle of named functions that (presumably) share state between them, it's very natural to assume that this implies relationships between how those functions will behave (even in the simplest examples, e.g. Java's Iterator has only two methods, but it also specifies a relationship between what those two methods return). And so it's very easy to write code that relies on those relationships, and then breaks when passed a bundle of named functions that does not conform to that relationship.
"then breaks when passed a bundle of named functions that does not conform to that relationship."

In that case, the fault lies with the thing that put unrelated functions together and passed them to a thing expecting them to be related. I'm not claiming that structural typing will somehow prevent programmers from deliberately writing wrong things, because I mean, what type system can make that promise? My point is that it is in my experience very rare for something expecting "something that can write bytes" to be accidentally passed "something that can write novels" or something equally unrelated, and then the world blows up in a bad way (that is, not just an exception thrown, because that's just a risk of dynamic languages, but actual bad things happening).

Dynamic languages can at least still get what I said wrong; in Go it's even harder because as others have pointed out, Write([]byte) and Write(NovelInput) Novel still can't cross. But even in dynamic languages, this isn't a problem I had.

Nothing can go wrong with a single function like Write - but in that case you don't need a structural type, you can just pass a write function. As soon as you pass a bundle of two or more functions, you're implying a relationship between them, and it's then easy to accidentally violate that relationship - for instance, a structural type will almost always admit a mutable implementation where the relationship breaks down if the thing is mutated in between calls to the two functions.
Like I said elsewhere in the thread, you can have the same problem with callbacks:

    func receiver(next func() bool, getItem func() Foo) {}
vs

    type FooIter interface { Next() bool; Item() Foo }

    func receiver(iter FooIter) {}
You're more likely to pass the wrong set of callbacks than to pass the wrong FooIter implementation since the type system doesn't require the callbacks to be related to the same data nor named any certain way.
Replied elsewhere.
You have the same issue if you pass multiple callbacks to a given function, except as previously mentioned, you're more likely to pass the wrong set of callbacks than to pass the wrong interface implementation (because interface methods are named must be bound to the same data).
Right, but you don't do that. The people who are happy passing functions around are not happy passing multiple callbacks around, by and large. (For example, rather than separate hasNext and next functions, you'd have a single function that returned an optional value).
I agree, but we're arguing about a hypothetical bug scenario that has probably never ever happened: accidentally implementing this sort of stateful interface. These stateful interfaces are already very rare (at least in Go) and accidentally implementing an interface is itself vanishingly rare (I don't think I've ever heard of this happening in practice). It doesn't make sense to me to take grievance with Go's interfaces (which offer strictly stronger protections than callbacks), but be okay with naked callbacks on the basis that "people tend not to use them in a stateful manner" (never mind the stateless scenario in which bare callbacks also provide weaker guarantees than Go's interfaces).
> we're arguing about a hypothetical bug scenario that has probably never ever happened: accidentally implementing this sort of stateful interface. These stateful interfaces are already very rare (at least in Go)

Any interface can be implemented by something that's accidentally (or intentionally) stateful - there's nothing that prevents that in Go. And a interface containing more than one function almost certainly has an implicit relationship between those functions that an implementation could accidentally violate.

> and accidentally implementing an interface is itself vanishingly rare (I don't think I've ever heard of this happening in practice).

Accidentally implementing an interface must happen all the time - not in the sense of unrelated functions having a name collision, but in the sense of implementing a type that looks like another type and not explicitly looking through all the interfaces that other type implements and what expectations they carry.

> It doesn't make sense to me to take grievance with Go's interfaces (which offer strictly stronger protections than callbacks), but be okay with naked callbacks on the basis that "people tend not to use them in a stateful manner" (never mind the stateless scenario in which bare callbacks also provide weaker guarantees than Go's interfaces).

Naked callbacks make it clear what the expectation is and where the responsibility lies. It's the same as the idea that bad encryption is worse than no encryption at all, or a datastructure that is "mostly threadsafe" is worse than one that immediately errors when used from multiple threads.

A bug I've seen more than once is: a function is passed what it thinks is an immutable list, expects to be able to iterate over it repeatedly and get the same items in the same order, but what it actually has is a read-only view backed by a list that is being mutated by a parallel thread. Another bug I've seen repeatedly is a function that assumes it's passed a sorted list, but actually gets passed an unsorted list.

The problem with structural typing isn't that completely unrelated things end up sharing a type. It's that you have no way to express the subtle distinctions between similar but different things, which are actually when having distinct types is most important.

I think it's really interesting to contrast this with Stroustroup's advice on concepts: "My rule of thumb is to avoid “single property concepts.”".

He gives an excellent example leading to this dictum: single property interface "Drawable". Let Shape implement drawable with widely expected results. But: let Cowboy implement Drawable -- now with disastrous consequences. The solution to this dilemma is as stated above, not to allow concepts/interfaces/{wave hands} to refer to a single object -- for example, a number is a concept which refers to multiple operations (i.e. methods).

To my mind, this feels right, albeit initially surprising. It makes me think that e.g. the "Neg" interface in Rust is wrong.

See: http://www.stroustrup.com/good_concepts.pdf

For golang, single method interfaces are recommended as preferable. The problem with huge interfaces or classes is they can always become larger! While if you compartmentalize single responsibility into separate components (may be several methods if applicable to the domain and design), each reason to change may be restricted to that bounded context only. If not, it's a signal design might need revisit.

The problem with "Drawable" is that it is property/data, and not messaging-based, thus breaking encapsulation, even if it was behaviour-based.

> The problem with huge interfaces or classes is they can always become larger!

That's definitely a good reason to prefer small interfaces; I hadn't thought of that but it certainly makes sense.

That said, I think we may be referring to different things when we use the word "property". Stroustroup is talking about properties in the mathematical sense, i.e. he's talking about the properties of the operation "draw()" in the concept Drawable.

Am I misunderstanding you? To my mind that's an operation and closer to "message" (emphasis on the verb aspect) rather than data or property.

"Drawable" as a name is pretty bland, like patterns. Do we ever really say "drawable" out loud?

So I suspect if we seek more meaningful objects and models, they become more active, have better names, improved design, etc.

If you ask a janitor about Drawable, will he say it's all about draw()? So I think it's kind of a dreary default that we need to avoid and focus on who the actors are, what messages they will pass, and the hidden treasures they encapsulate (don't show, but tell!).

Frequently in Go I find that single-method interfaces are easier to work with when made into a closure. Instead of `type Foo interface { Foo() }`, I often find `type Foo func()` easier to work with. There are probably exceptions to this; I'm mostly just voicing my own observation.
Neg is used to implement operator overloading for unary minus; it's unclear how you would implement this functionality without only doing one thing.
Thanks for clarifying that; I was actually aware of the link to syntactic sugar and it's probably true that you want to control that independently. After all, who doesn't want to hijack unary minus for the purpose of as hackneyed rendering of EBNF? C++ certainly does it :)

Anyway a serious answer to your comment would be to create a field or ring concept. This would unify some or all of the arithmetic operations.

A neat bonus would be the ability to overload the one and zero literals for the set. So in field of nxn matrices on real numbers 0 is the zero matrix and 1 is the identity.

> After all, who doesn't want to hijack unary minus for the purpose of as hackneyed rendering of EBNF?

Ha!

> Anyway a serious answer to your comment would be to create a field or ring concept.

Yeah, this is true. We really, really struggled to create a set of traits for numerics, even regardless of these more advanced ideas... so it's unclear this would work. But it's certainly closer than I had thought :)

It has already caused several issues in golang, and this is an example: https://github.com/golang/go/issues/16474
That’s a different issue. This is not just using structural interfaces but downcasting to even lower interfaces without warning. That is, the method lies about what it’s looking for.
(comment deleted)
It will cause issues if a type implements the interface where it didn't mean to.
Anecdotally, this happened to me today
This is a poorly written article. It requires that one already understands the concept to even follow along, while providing no additional information nor conclusions.
I 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.

I 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.

I longed for an article as well written as this one.
I 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

If 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.
Structural 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".