Shared publicly  - 
 
In this post by +Brett Slatkin, he talks about the orthogonality of features in Go (he calls it "emergent behavior", which is the wrong term). Orthogonality of features in a programming language is hard to achieve, and we worked hard to achieve it, so I'm glad he appreciates that.

I was struck, though, by this comment from John Haugeland:

"Gasp! A bunch of stuff you can do in most languages, and a misunderstanding of what emergent behavior is, presented by an author who doesn't realize this is all old hat. Truly, that is the power of go."

He means this a snide dig, of course, but it's true. It is old hat. The thing is, he doesn't appreciate that the service he's using to write that comment is written by programmers, not as facile in some of the more abstract languages and mathematical notions as he is, for which these ideas are not old hat. And making these (and other) ideas available to working programmers as opposed to programming language researchers is indeed the "power of go". And that, too was deliberate.

So I accept his barb, and acknowledge it with a smile.

Gophercon was awesome, by the way.
109
9
Ivan Jager's profile photoAxel Wagner's profile photoAndrew Wahbe's profile photoRobert Griesemer's profile photo
14 comments
 
Thanks for addressing that comment! Makes me feel better.

Also understood that "emergent behavior" is wrong technically. I wanted to use a term that translates to other disciplines, like economics, to make it more clear to newbies. I find myself similarly using "is a" and other OOP terms even though they're technically wrong. I'll put a PS in the post next time.
 
My biggest problem with the "implicit interface" stuff is that it pretends that an interface is just a list of methods and their signatures (and properties, fields, operator overloads, etc, in other languages). But that's not what an interface is. An interface is a list of methods (etc), plus the contract that those methods are supposed to follow.

e.g. if you have an interface that declares two methods "open" and "close", you don't know if the contract is "you can call open and close as many times as you want in any order" (because the class operates on a number of windows and it just opens and closes them as desired), or instead if the contract is "you must first call open, then later you must call close, and there must not be any other calls to open or close" (because the class operates on a file handle or some such).
 
+Ian Hickson though this is factually correct, I think it rarely poses any kind of real problem, since the contractual part of the interface is usually done in the documentation (io.Reader and io.Writer are a good example for what you describe, because there actually is a contract just in how a specific method behaves, i.e. in what cases n < len(buf) can be returned).

And if you pass a value as an interface, you have to know what interface ("what" as in io.Reader is a different interface from foo.Reader, even if they specify the same methods)  you are passing, so in practice, as a programmer, you have to be aware of the detailed behaviour of the methods anyway.

Is there any language where you can actually specify such contracts in the type-system? Because I find it hard to imagine a type-system that makes these kind of arbitrary complex contracts possible to express.
 
Sure, modern Pascal for example lets you specify the difference. You call one interface "TWindowOpener" and the other one "TFileOpener" and you only accept the right kind of opener.

It's the implicit part that's the problem. If you just name your interfaces and make it all explicit, you get the same power, with more strict type checking. (It's the compile-time type checking I want.)
 
So you acually don't express any kind of contract, you just have to make it explicit, what contract you fulfill (I don't know pascal, but that's what I took from your reply). So okay, yes, that prevents some inadvertant wrongdoing. But as I said, I don't think this really is a problem in practice, because you would still need (as a programmer) get from "this method takes an io.File" to "I'll just pass it an xdg.Window, because they define the same methods". Dunno, maybe it just seems hart to imagine for me. But I agree, that explizit implementation would provide the same power to a programmer while making this more difficult.

I think the implicit implementation thingy has another reason though, that russ cox details in the "A tour of go" talk: It prevents dependencies, where there really are none.

But, oh well. go isn't perfect and it doesn't have to be ;-) I guess that's just one more point where you have to decide, what is important to you about a language.
 
+Ian Hickson Does Pascal warn you if you say you are implementing TWindowOpener but are really implementing TFileOpener? How does it know which contract corresponds to each?

You're also conflating implicit/explicit with structural/nominal. Go's interfaces are explicit and structural. You do need to explicitly say what interface a method's arguments need to satisfy, but which name you use to describe that interface doesn't matter.

Nominal typing (in which you list the names of all the interfaces you are implementing) has the problem that you typically don't know all the names for the interfaces you are implementing, and even if you do, you often don't want to depend on other libraries merely because they define another name for your interface. For example, Java 8 introduced the Function interface with an apply method, exactly like the one that's been in the Scala library for years. So now there are a bunch of functions that all implement the same interface, with the same contract, except some call it java.util.function.Function while other's call it scala.Function1 so they are treated as incompatible.
 
+Brett Slatkin As +roger peppe pointed out in his G+ post, I was wrong; even opaque (unexported) methods of exported interfaces don't prevent such interfaces from being implemented externally. I think this was definitively a surprising observation that we made quite late (and in fact I had forgotten it again).

What I did mention as surprising in the panel was that one could assign a nil pointer of a concrete type to an interface variable, and then that interface variable would not be nil. It's a common novice mistake. This was clearly intended when we originally sketched out the implementation of interfaces as pairs of a type descriptor (the dynamic type) and actual value stored (the dynamic value); but the consequences were not fully appreciated at first.

+Ian Hickson The "implicit interface" problem you are alluding to can be alleviated by adding your (exported) "qualifying" method(s). If you wanted to document that clients don't just implement an interface but also its behavior, simply add a dummy method that documents that behavior, say: ImplementsFooBehavior(). Of course it doesn't enforce the behavior but it's no worse than having a specific type system feature such as an "implements" keyword. This is exactly one of those mechanisms that can be "emulated" fairly nicely with existing functionality and there's no need to have extra mechanisms.
 
The argument that you don't need explicit interface names because people aren't going to make that kind of mistake is the same as the argument that you don't need explicit types on variables because nobody is going to accidentally think that the integer variable they're dealing with is a string. You can follow that kind of argument all the way to typeless languages like JS or Perl. Sure, they have their place, but if you like having your compiler do strict type checking (as I do), then you don't want that kind of language. 

I agree that I should be saying structural vs nominal rather than implicit vs explicit. I think using structural strong typing rather than nominal strong typing is the mistake.

I agree that languages with nominal strong typing would benefit from a mechanism to tell the compiler "these two structurally identical types are also nominally equivalent, please proceed as such". (Personally, since my projects don't involve many external libraries, I run into the problem of confusing one type for another more often than I run into the problem of multiple libraries having identical incompatible types, though.)

Adding a no-op method to an interface for the sole purpose of adding nominal typing is an ugly hack, IMHO.
 
+Ian Hickson Pretending you can't have strict type checking without explicit types on all your variables is a straw man since type inference was invented. The two are pretty much orthogonal now. For example, ocamlc will prevent you from accidentally adding a float to an int even if neither variable has explicit type annotations whereas javac won't even issue a warning even though it requires both to have explicit types. (Same with string and int actually.)

Also, I think Go is intended to scale beyond projects that don't require external libraries...

Out of curiosity, have you ever used a language that supported both structural and nominal typing?
 
Is there a good authoritative definition of "orthogonality" as you are using it? A quick search didn't yield one. From what I can piece together from your usage it does remind me a lot of "supervenience"  which is often used to describe the relationship between emergent properties and their base properties -- so I can see why that term (emergent) was used.
 
Orthogonality in PL design goes all the way back to Algol 68 (and possibly earlier): http://en.wikipedia.org/wiki/Orthogonality#Computer_science . van Wijngaarden's description in that article is very succinct and describes the philosophy behind the Go design perfectly.

The same article describes what is meant with "emergent behavior" resulting from (in our case) orthogonal base properties.

(I see little connection with "supervenience").
Add a comment...