News for hackers. Stuff that matters.
Re:Too Much Go Misdirection by silverwind on Monday May 19, 2025 @03:01PM (#44033508)
A good API should just accept either,e.g. the union of []byte and io.Reader.

Both have pros and cons and those should be for the user to decide.

Re:Too Much Go Misdirection by thayne on Monday May 19, 2025 @03:19PM (#44033718)
Ah, but go doesn't have union types.
Re:Too Much Go Misdirection by Zambyte on Monday May 19, 2025 @04:10PM (#44034262)
One option would be to accept an interface{} and then switch on the type.
Re:Too Much Go Misdirection by stouset on Monday May 19, 2025 @04:40PM (#44034601)
It's frightening how quickly the answer in golang becomes "downcast to interface{} and force type problems to happen at runtime".
Re:Too Much Go Misdirection by throwaway894345 on Monday May 19, 2025 @05:30PM (#44035100)
You don’t need to downcast to interface, io.Reader is already an interface, and a type assertion on an interface (“if this io.Reader is just a byteslice and cursor, then use the byteslice”) is strictly safer than an untagged union and equally safe with a tagged union.

I wish Go had Rust-like enums as well, but they don’t make anything safer in this case (except for the nil interface concern which isn’t the point you’re raising).

Re:Too Much Go Misdirection by wredcoll on Monday May 19, 2025 @10:46PM (#44037277)
Presumably the questions that have simple and easy answers don't get long comment chains.
Re:Too Much Go Misdirection by throwaway894345 on Monday May 19, 2025 @05:33PM (#44035128)
An io.Reader is already an interface, so you can already switch on its type.
Re:Too Much Go Misdirection by Zambyte on Monday May 19, 2025 @06:52PM (#44035779)
My comment is explaining how

> A good API should just accept either,e.g. the union of []byte and io.Reader.

could be done. Can you elaborate on how the fact that io.Reader is an interface lets you accept a []byte in the API? To my knowledge, the answer is: you can't. You have to wrap the []byte in an io.Reader, and you are at the exact problem described in the article.

Re:Too Much Go Misdirection by throwaway894345 on Tuesday May 20, 2025 @10:00AM (#44041812)
I see what you’re saying. You’re correct that you can’t pass a []byte as an io.Reader, but you can implement io.Reader on a []byte in a way that lets you get the []byte back out via type switch (the problem in the article was that the standard library type didn’t let you access the internal []byte).
Re:Too Much Go Misdirection by ithkuil on Wednesday May 21, 2025 @03:05AM (#44049002)
An idiomatic way to approach this would be to define a new interface, let's call it Bytes with a Bytes() []byte method.

Your function would accept an io.Reader but then the function body would typeswitch and check if the argument implements the Bytes interface. If it does, then call the Bytes() method. If it doesn't then call io.ReadAll() and continue to use the []byte in the rest of the impl.

The bytes.Buffer type already implements this Bytes() method with that signature. By the rules of Go this means it will be treated as an implementation of this Bytes interface, even if nobody defined that interface yet in the stdlib.

This is an example of Go's strong duck typing.

Re:Too Much Go Misdirection by Zambyte on Thursday May 22, 2025 @10:52PM (#44069432)
That's really interesting, thank you for explaining that! Somehow I've never thought to implement interferences to describe types that already exist.
Re:Too Much Go Misdirection by foldr on Tuesday May 20, 2025 @06:16AM (#44039854)
You can just expose two different functions, one of which takes a byte slice and one of which takes an io.Reader.

Given how the code works (it starts by buffering the input stream), the second function will just be a few lines of code followed by a call to the first.

Perfect example of how complex type systems can lead people to have unnecessarily complex thoughts.

Re:Too Much Go Misdirection by throwaway894345 on Monday May 19, 2025 @05:24PM (#44035038)
Nah, a good API doesn’t push the conditionals down. You don’t need to pass a union to let the user decide, you just need to present an API for each (including a generic implementation that monomorphizes into multiple concrete implementations) https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-do...