Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I/O isn't an object, it's an interface (Go has no objects). There is nothing magical about I/O or any interface--they are just functions--so this change would require extra magic to be implemented which I strongly oppose.

One of the beautiful things about Go is the designers carefully chose to confine language magic to only a few fundamental areas (channels, goroutines, arguably maps) and resisted going beyond that. Go approximates "C plus Concurrency and Better Data Structures" and nothing more.



On the contrary, the magic is required because things like select and channels and coroutines are implented at the language level in go. If they were a library with proper extension points and, sure possibly languge sugar on top, it would be doable.


That is very nearly what they are.

The compiler actually just transforms everything channel related into function calls within the runtime package. The majority (probably > 99%) of the implementation of channels and select is Go itself.

E.g., see: https://golang.org/src/runtime/chan.go


"There is nothing magical about I/O or any interface--they are just functions--so this change would require extra magic to be implemented which I strongly oppose."

"Extra", yes, "magic", debatable. (Which, to be clear, I mean straight, not as a rhetorically-weakened pure disagreement.) There is precedent for using the io.Reader and io.Writer interfaces but allowing structs that implement those interfaces to implement additional interfaces to gain additional capabilities already, especially in the io.Copy function: https://golang.org/pkg/io/#Copy (Click on the Copy name to pass through to the underlying implementation, which bounces through to another function which should still be on your screen, and the first few lines show the special casing.) Though I also find myself with some frequency having to accept a Reader or a Writer and examining it to see if it's also an io.Closer, because if I want to wrap a new io.Reader around an underlying io.Reader for some reason, something I do quite a lot, it's important to propagate proper closing behavior, especially when wrapping with gzip or something else that really needs to be closed and not just sort of trail off at an arbitrary point.

Making io.Reader "just work" in a switch statement would be magic, but using a similar technique to the above it might be possible to offer a new "io.ReadableChan(io.Reader) <-chan struct{}" (or the obvious WritableChan extension) function that files or sockets would implement using a polling mechanism, and everything else would get an automatic new goroutine created that used just io.Reader's existing interface. This could offer a zero-buffer-until-IO implementation without having to modify io.Reader. The resulting channel coming back from ReadableChan might have to be a new sort of magic channel internally, but that would be an internal detail that shouldn't ever rise up to your code.

Alternatively, it might be a "io.ReadableChan(func() []byte) <-chan []byte", waiting internally until it can be read then determining the []byte to use for the read by invoking the provided function. I'm just spitballing here, not preparing a change proposal. (For such a thing I'd also want to consider whether that should be "func () ([]byte, error)", for instance.)

This is one of those places where Go favors practicality over purity, and is very much not channeling Haskell. Checking whether an interface value implements another interface is probably something you shouldn't be doing all the time, but it's not inherently un-Go-like or anything. You're just taking more responsibility. Much like how you are supposed to share memory by communicating but Go will still let you communicate by sharing memory if you want.


The epoll/select syscalls and the file descriptors for TCPConns are exposed, so a lot can be done in third-party packages; I linked some relevant stuff in another comment. It's kinda hard for me to imagine explicit wait APIs being deeply integrated with std, though; maybe if things reached the point where sitting on buffers became the bottleneck for a lot of apps (in which case, dang).


ReadReady() could be made part of an interface, it doesn't necessarily need to be magic.

That this pattern exists and it would be nice if there was a solution, I don't really care about the implementation.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: