
Is Go Duck-Typed? - bschaeffer
https://bionic.fullstory.com/is-go-duck-typed/
======
mbell
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](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)

~~~
loopz
Duck 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.

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

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

~~~
derefr
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...](https://joearms.github.io/published/2013-11-21-My-favorite-
erlang-program.html) ]. 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.

~~~
gowld
Arguing about definitions is old and boring.
[https://en.m.wikipedia.org/wiki/Type_system](https://en.m.wikipedia.org/wiki/Type_system)

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

------
nemith
I like to call it Quack typing as interfaces are based on behavior and not
attributes.

~~~
IshKebab
No they aren't, they're matched based on method names.

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

------
jolux
No, it is not duck typed. It is structurally typed, as @mbell explains.

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

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

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

~~~
jerf
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.)

~~~
throwaway894345
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?

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

~~~
jerf
"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.

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

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

~~~
lmm
Replied elsewhere.

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

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

------
iddan
I longed for an article as well written as this one.

------
smoyer
Betteridge's law of headlines would say "no" (as would @mbell in this
conversation). Curious, the author says "undefined". @mbell FTW!

