Live data from Hacker News

Is Go Duck-Typed?

bionic.fullstory.com

61–70 of 89 posts

Re: Is Go Duck-Typed?

#61
post #2

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

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

Re: Is Go Duck-Typed?

#62
post #2

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

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/

This is an amazing observation for someone unfamiliar with structural typing. It's probably also why python programmers tend to enjoy Go.

Re: Is Go Duck-Typed?

#63
post #41
post #7

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.

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.

Re: Is Go Duck-Typed?

#64
post #5
post #2

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.

Re: Is Go Duck-Typed?

#65
post #35
post #2

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

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 dyna…

OCaml has structural typing with full type inference:

   # let f obj = obj#foo 1 2;;
   val f :  int -> 'a; .. > -> 'a = 
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.

Re: Is Go Duck-Typed?

#66
post #53

Earlier quoted context omitted.

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?

On JVM level, yes. But on Java level, the type of the method is structurally matched to the type of the interface.

Re: Is Go Duck-Typed?

#67
post #29

Earlier quoted context omitted.

> 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 ty…

> 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 k…

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.

Re: Is Go Duck-Typed?

#68

Earlier quoted context omitted.

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.

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.

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.

Re: Is Go Duck-Typed?

#69
post #2

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

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.

Re: Is Go Duck-Typed?

#70
post #49

Earlier quoted context omitted.

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'…

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

Post reply on HN