1. 18
    1. 11

      Lots of other things to comment on but I firmly think interfaces-as-structural-types is one of Go’s greatest strengths. To put it in the “bad” group is upsetting and kind of made me discount the rest of the article.

      1. 6

        I think it’s a philosophical difference:

        Some developers write code to document to coworkers what they are doing, and some developers just want things to compile with the least effort possible.

        1. 2

          It’s not a matter of least effort. Go is one of the first languages I know of that was primarily designed for large teams of engineers instead of individuals. Heavy focus on compile time, gofmt, not allowing compilation with unused variables, etc, all directly stem from this approach. Structural typing specifically reduces cross-team friction. Go will often make decisions that incur individual overhead to reduce overall team overhead

          1. 5

            Not sure I agree on this.

            Compilation is not particularly fast, even compared to more more modern languages like Algol, its dependency management is a disaster, it’s error handling ignores the last few decades of lessons learned and the amount of code duplication it forces upon developers makes it hard to maintain.

            I think it does well in terms of helping Google’s requirements of having all code in a large mono-repo, and enabling people who have no practical experience to produce code.

            1. 2

              Whether or not they succeeded at being fast wasn’t my point (though my position is they did succeed). My point is the kinds of things they emphasized in language design. Russ Cox argues that compilation speed is one of the reasons they don’t have generics, for instance.

              Dependency management doesn’t matter with large teams in a mono repo, yeah, and the code duplication to me felt like it would be an enormous issue when I started but in practice, half a million lines of code later, it doesn’t come up nearly as much as you’d think.

              1. 2

                Compilation doesn’t have to be slow just because generics are involved, the author of D demonstrated that fairly well. I think this is rather an issue of generics not having been invented at Bell Labs (or decent error handling in this regard).

                I’m not sure why “dependency management doesn’t matter if you are a Google employee” should be a convincing argument for programming-in-the-large for the millions of non-Googlers out there.

          2. 2

            Structural typing specifically reduces cross-team friction.

            Can you talk about how structural typing accomplishes this?

            EDIT: Ah, I see you answered this in another thread.

      2. 3

        Languages are funny. I’d consider defer to be a bad idea elevated to a language feature, but it’s in the “good” group 😀

      3. 2

        Can you explain why you like this idea?

        1. 4

          Sure! Let’s say someone writes a library you use frequently but writes it such that it does not explicitly implement any interfaces, as the author of the above post prefers. Maybe you use a library called Beep like this:

          type Beeper1 struct { ... }
          func NewBeeper1() *Beeper1 { ... }
          func (b *Beeper1) Beep() { ... }

          You are writing your library, but want to support multiple implementations of Beepers. Maybe there’s another beeper (for a test, or another library, or something else) that also has a Beep() method. So you write your code to just expect

          type Beeper interface {

          Now you can use the third party code, your code, your test code, etc, without having to change the third party code upstream to implement your interface.

          This is a super contrived example, but as your codebase and team grows larger, this becomes incredibly useful for reducing friction in having teams of engineers work together with minimal stepping on each other’s toes.

          Ultimately, I describe Go’s structural typing system to Python programmers like the static typing equivalent of Python’s “duck typing” principle, which is, if it looks like a duck and quacks like a duck, just treat it like a duck. Coming from statically typed languages that require you to list what interfaces a concrete instance implement, Go not requiring that dance felt like a huge reduction in friction to me.

          1. 2

            I guess, to me, it feels like a strictly worse approach than what Rust has with traits, or Haskell with typeclasses, because there’s no explicit guarantee that a “Beeper” is actually abiding by the contract of the “Beeper” interface. It could have a “Beep” method that actually nukes Vienna. There’s friction to implementing a trait or typeclass for a new type, but there’s also value in it. If I have explicitly implemented a trait, there’s documentation of the type’s usage in that context as well as of its conformance with the interface.

            1. 3

              A frequent pattern in Go to get some of that functionality if you want it is to write something like

              var _ InterfaceName = (*ConcreteType)(nil)

              which simply adds a compile time assertion that ConcreteType does indeed implement InterfaceName

              Certainly does nothing to constrain the behavior, but I’m super happy with that (optional) middle ground

            2. 3

              There exists a spectrum: let’s say that on one extreme, it’s maximum programmer friction with minimum risk of mis-use; and on the other extreme, minimum programmer friction with maximum risk of mis-use. Go puts a marker down closer to the latter extreme, judging friction to be a worse evil than risk for their context, and providing some affordances (like the one-liner jtolds mentions) to mitigate some of those risks via convention.

              I think no position on the spectrum is “strictly worse” than any other. It is a question of trade-offs to satisfy particular programming contexts or requirements. I think this is the same for any technical decisionmaking.

              Go makes a lot of decisions this way. (Not all, and there are warts for sure — but many.) I think it is a nice and refreshing change from where most languages (like Rust) decide to land, and I think Go’s success in the market proves it is a viable, or maybe even preferable, compromise-point for many users.

      4. [Comment removed by author]

    2. 1

      I think academic discussion of programming languages should seriously start taking into consideration programming language success. It seems to me that academically nice features makes a programming language typically fail in terms of adaptation rates. Why is that?

      Go is wildly successful in some fields, such as devops, having displaced Python in many places. Some people are trying Rust in that space (Habitat for instance), but they don’t seem to thrive.

      Could it be that good PL features are very rarely a useful thing to have?

      1. 2

        Academically nice features are almost by definition cutting edge or rely heavily on theory or give some interesting property. The average software engineer doesn’t care that uniqueness types make memory management deterministic…they care that now they can’t use a variable twice.

        I think it might be summed up as “academic languages target computer scientists, successful languages target software engineers.”

        1. 2

          I’d group some historically successful languages somewhere in between those, as quasi-academic. C is probably the best example. It came out of a Bell Labs research project staffed by a bunch of PhDs. Pretty explicitly written by computer scientists, for computer scientists, not written to serve the needs of software engineers working on line-of-business software (unlike, say, COBOL). And even when the Bell Labs folks got external collaborators, their collaborators were… more researchers, at UC Berkeley (hence BSD). Its research origins were admittedly in systems research rather than programming languages research, though, which leads to different design goals. For example, they wanted an easy-to-implement compiler.