Live data from Hacker News

Stunned by Go

how-bazaar.blogspot.co.nz

11–20 of 115 posts

Re: Stunned by Go

#11
post #9

> The standard http library was calling Close on my io.Reader. This is not expected behaviour when the interface clearly just takes an io.Reader (which exposes one and only one method Read). So if I implemented my own object with a Reader interface and passed it to this HTTP library, could it find some other method on my object named Close() and it could call it? Is it pure duck typing happening here, or is the HTTP…

> So if I implemented my own object with a Reader interface and passed it to this HTTP library, could it find some other method on my object named Close() and it could call it?

Yes, and that's what's happening here. It can try to convert your value to a value of type Closer, which is an interface that implements a method Close(), and it can use that method. This conversion will succeed if the value you pass in has a method Close() (taking no arguments) -- Go has a strong "loose coupling" philosophy; there is no "implements."

For example, you can do:

nfoo, ok := foo.(myCustomInterface)

where foo is any value. If foo implements myCustomInterface (indicated by ok being True), regardless of whether that interface is exported or not, or whether the person behind foo intended to implement it, then you will be able to call myCustomInterface's methods on nfoo. It's not exactly duck typing, but it does mean that taking a value implementing SomeInterface is no guarantee that you can only use the methods defined by SomeInterface.

See my explanation here: https://news.ycombinator.com/item?id=6060627

Re: Stunned by Go

#12
This behavior is explicitly defined in the godoc for io. I don’t see why the author should be "stunned" by a function doing exactly what it says it does.

Re: Stunned by Go

#13
This is caused by an unfortunate bit of code in the standard library, but is hardly representative of a fundamental problem in Go as the article implies (unless you view casting and interface coercion as a fundamental problem, and I know some people do, but that's basically a different debate since Go does not pretend to be a purely statically typed language by any stretch of the imagination).

I've been using Go regularly for nearly two years now and haven't run into an issue quite like this either with the http package (what he's doing that causes this to be a problem is legal and should work, but is not a common use case) nor with any other bit of the standard library.

Re: Stunned by Go

#14

This is caused by an unfortunate bit of code in the standard library, but is hardly representative of a fundamental problem in Go as the article implies (unless you view casting and interface coercion as a fundamental problem, and I know some people do, but that's basically a different debate since Go does not pretend to be a purely statically typed language by any stretch of the imagination). I've been using Go regu…

It depends on what your definition of "fundamental problem" is. If being able to treat a value differently than it was received, or to perform computations that you shouldn't be able to, e.g. impure actions inside a pure function, then very few languages satisfy that property. Even Haskell isn't safe because of unsafePerformIO--you'd need Safe Haskell, at the very least.

Go has never tried to be, and the authors have never tried to give people the impression that it is that kind of language. Some of the safety comes from the language e.g. doing array bounds checking, not letting you manipulate memory manually, etc., but the rest comes from discipline and idiomatic code. (Same thing as not using 'undefined', 'fromJust', 'unsafePerformIO', etc. in Haskell unless you're absolutely sure they won't do harm--they are too useful to remove, even if they are the sources of many headaches.)

Re: Stunned by Go

#15
post #7
post #3

Anyone proficient in GO would care to provide the minimal code that illustrate that extremely weird behavior ? I find that post so disturbing that i still believe i've missed something.

An example: http://golang.org/src/pkg/io/io.go?s=11741:11801#L336 Line 336. io.Copy takes a writer and a reader, but actually asserts whether they implement ReaderFrom/WriterTo, then use their methods, ReadFrom and WriteTo, if they do. (Same thing with the http package checking if the reader implements Closer, and calling Close if it does, though admittedly 'we should be able to call Close' is a much more unsafe assu…

This io.Copy behavior seems like an "optimization"... less explicit than I would like but sticks to the idea of an io.Copy.

Having a resource you own Closed on you isn't an optimization, it is a behavior change.

Re: Stunned by Go

#16
post #7

Earlier quoted context omitted.

An example: http://golang.org/src/pkg/io/io.go?s=11741:11801#L336 Line 336. io.Copy takes a writer and a reader, but actually asserts whether they implement ReaderFrom/WriterTo, then use their methods, ReadFrom and WriteTo, if they do. (Same thing with the http package checking if the reader implements Closer, and calling Close if it does, though admittedly 'we should be able to call Close' is a much more unsafe assu…

This io.Copy behavior seems like an "optimization"... less explicit than I would like but sticks to the idea of an io.Copy. Having a resource you own Closed on you isn't an optimization, it is a behavior change.

I agree. I think I edited my comment to specify that it's a much more unsafe assumption just before you posted your comment.

Re: Stunned by Go

#17
post #7
post #3

Anyone proficient in GO would care to provide the minimal code that illustrate that extremely weird behavior ? I find that post so disturbing that i still believe i've missed something.

An example: http://golang.org/src/pkg/io/io.go?s=11741:11801#L336 Line 336. io.Copy takes a writer and a reader, but actually asserts whether they implement ReaderFrom/WriterTo, then use their methods, ReadFrom and WriteTo, if they do. (Same thing with the http package checking if the reader implements Closer, and calling Close if it does, though admittedly 'we should be able to call Close' is a much more unsafe assu…

That is SO disturbing... They don't even check whether the parameter implements another interface, they do the check directly on the method by callin it... That's so ugly...

Reminds me of my surprise when i realized the math module only worked on float types, and that min/max only existed for some numerical types but not others. The thing in itself probably isn't a big deal, but it really questions the ability of the language to model complex relationships often found in business processes.

Re: Stunned by Go

#19
post #17
post #7

Earlier quoted context omitted.

An example: http://golang.org/src/pkg/io/io.go?s=11741:11801#L336 Line 336. io.Copy takes a writer and a reader, but actually asserts whether they implement ReaderFrom/WriterTo, then use their methods, ReadFrom and WriteTo, if they do. (Same thing with the http package checking if the reader implements Closer, and calling Close if it does, though admittedly 'we should be able to call Close' is a much more unsafe assu…

That is SO disturbing... They don't even check whether the parameter implements another interface, they do the check directly on the method by callin it... That's so ugly... Reminds me of my surprise when i realized the math module only worked on float types, and that min/max only existed for some numerical types but not others. The thing in itself probably isn't a big deal, but it really questions the ability of the…

> That is SO disturbing... They don't even check whether the parameter implements another interface, they do the check directly on the method by callin it... That's so ugly...

That's incorrect. The code you see at the top:

    if rt, ok := dst.(ReaderFrom); ok {
            return rt.ReadFrom(src)
    }
means: "If dst implements ReaderFrom, then call ReadFrom on this value of that type."

You can't arbitrarily call methods that may or may not exist. You can check if a value implements an interface by doing "newVal, ok := val.(suspectedInterface)", and checking if ok is true, or by doing "newVal := val.(suspectedInterface)" and living with a runtime panic if it doesn't implement the interface, then call a method in that interface on the new value newVal, which will be of type suspectedInterface. (If val doesn't implement suspectedInterface--i.e. ok is false--then newVal will be the zero value of suspectedInterface.)

Re: Stunned by Go

#20
post #5

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…

Thanks for the informative comment.

> 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!)).

Post reply on HN