Wow, this is pretty unfortunate. Also a very good example of why so many people maintain that Go is not nearly as statically typed as languages like Haskell or OCaml: casting (essentially subverting the type system) is very common, and limits most of the type system's advantages.
In large part this is because Go's type system is not very expressive. I would just like to point out that OCaml did the same things as Go (i.e. structural sub-typing). But with (global) type inference. And variants (including structural subtyping for those too). And generics (parametric polymorphism). Years before Go was developed.
In OCaml, this sort of thing probably would be much less likely to come up. After having used the OCaml object system a bit (but still more than most people!), I am actually very happy with it. It certainly has issues--oh boy, does it ever--but the issues are superficial. They are the result of very limited manpower in the project, not fundamental shortcomings of the design.
In OCaml, the compiler can figure out what methods you used without your telling it. So it would automatically realize that the function called the close method, and infer the appropriate type. You wouldn't even have to write an interface (called a class type in OCaml) for it. If you did write an interface, but it did not expose a close method, the code would give you a type error.
I suppose you could try to do some sort of runtime casting and duck-typing, but I am not sure how. It would certainly not be a common thing to do, at all! Instead, the type would reflect exactly how the object is used in the function. It would be more explicit, more predictable and safer.
And, harping back on the same theme, it would be fully inferred for you.
> Wow, this is pretty unfortunate. Also a very good example of why so many people maintain that Go is not nearly as statically typed as languages like Haskell or OCaml
I'm confused why "so many people maintain that" -- not because its a position I disagree with, because its not really a controversial position such that you'd need to "maintain" it.
Go is, pretty overtly C-like, but with some language-level concurrency features, plus type system features -- interfaces and methods and reflection -- to support OOP (though its definitely not a class-based OO language) and dynamic typing, plus GC, plus filing some of the sharp edges off of pointers.
It doesn't even begin to pretend to be like OCaml or Haskell in terms of strict typing.
> So it would automatically realize that the function called the close method, and infer the appropriate type.
So, if i understand this correctly, OCaml would infer that the function takes "an object with write() and close() methods" because it uses both methods in its definition. That's reasonable. But wouldn't that prevent it from accepting objects that don't have a close() method?
Wouldn't the proper type for the parameter of this http library be something like "an object with a write() method or (an object with write() and close() methods)" (i should have used another notation for this hehe)?
I think the biggest problem in this case is no so much the weakness of Go's type system, but the design decision of mixing two different behaviours (that require two different type signatures) in the same method in the http library. It seems there should be one method that takes a Reader and only calls Read() and another method that takes a ReaderCloser and calls both Read() and Close(), it can even delegate to the first one (assuming, of course, that Go accepts Reader as a valid subtype of ReaderCloser (i would hope so!)).
You're right; OCaml would have to supply two different functions, one that takes an object that exposes < close : (); write : string -> (); .. > and another that takes < write : string -> (); .. >. Reproducing the Go system would require the ability to, at runtime, see whether an object of type < write : string -> (); .. > can be downcasted to < close : (); write : string -> (); .. >, which would require more runtime type information than OCaml currently supplies. Some people have made proposals to implement such downcasting (throwing an exception if the downcast fails), which would permit the Go-like implementation, but as far as I know, such proposals haven't been met with much enthusiasm.
> but as far as I know, such proposals haven't been met with much enthusiasm.
Well, if the proposed use cases are similar to this http Go library, then i think i can see why that's the case. As just adding a close: () method to a type could mean that code that uses that type now behaves differently.
I'm not sure if this is what you're talking about, but if you want to see an example of "runtime casting and duck typing" in Go, have a look at my torrent tracker project:
What I've linked to is a filter that gets put in front of a controller. -- To keep some modicum of type-safety while making use of interfaces, I've added these TestContext() methods to my interface.
Basically, when a controller is registered with one of these chains, the entire chain is checked to make sure that its dependencies are satisfied. (For example, this "flash" filter requires the "session" chain, because it uses session storage for the message being displayed to the user.)
The controller has a similar test; and these runtime penalties are only incurred once when the route is registered. So the app will panic immediately if the types needed for the chain to work aren't present.
It actually works out quite nicely: but you _need_ to write these tests or it falls flat on its face. -- I like to approach it as a form of test-driven development whenever I add a new filter or controller.
In large part this is because Go's type system is not very expressive. I would just like to point out that OCaml did the same things as Go (i.e. structural sub-typing). But with (global) type inference. And variants (including structural subtyping for those too). And generics (parametric polymorphism). Years before Go was developed.
In OCaml, this sort of thing probably would be much less likely to come up. After having used the OCaml object system a bit (but still more than most people!), I am actually very happy with it. It certainly has issues--oh boy, does it ever--but the issues are superficial. They are the result of very limited manpower in the project, not fundamental shortcomings of the design.
In OCaml, the compiler can figure out what methods you used without your telling it. So it would automatically realize that the function called the close method, and infer the appropriate type. You wouldn't even have to write an interface (called a class type in OCaml) for it. If you did write an interface, but it did not expose a close method, the code would give you a type error.
I suppose you could try to do some sort of runtime casting and duck-typing, but I am not sure how. It would certainly not be a common thing to do, at all! Instead, the type would reflect exactly how the object is used in the function. It would be more explicit, more predictable and safer.
And, harping back on the same theme, it would be fully inferred for you.
The best of both worlds, really.