|
The problem with that is I have to care how `file` works. Here's how to read a whole file then loop over the lines: file, err := ioutil.ReadFile("data.txt")
// some error handling
for _, line := range strings.Split(file, "\n") {
fmt.Println(line)
}
Here's how to stream a file one line at a time: file, err := os.Open("data.txt")
// some error handling
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
// more error handling
}
Here's how I, at least, would like it to work: fileContents := file.Read("data.txt")
for _, line := range fileContents {
fmt.Println(line)
}
if fileContents.Err() {
// some error handling
}
fileContents := file.ReadStreaming("data.txt")
for _, line := range fileContents {
fmt.Println(line)
}
if fileContents.Err() {
// some error handling
}
The critical point is that I don't want to care whether `file` is a byte slice or a byte buffer, and Go doesn't let me not care. I want to be able to write code that deals with "enumerable data of some sort", once, and then works no matter how the caller decides to provide that data.Go (intentionally) makes it exceedingly difficult to obscure how a piece of code works from the rest of the codebase, and I personally think that's a fatally poor design decision. In my experience, it makes it difficult to decouple modules since code often has to be at least somewhat aware of quite a few implementation details of a library in order to use it correctly. It makes it really difficult to build higher level abstractions that don't leak. I have a much harder time in Go getting away from thinking in `int`s and `floats` and staying terms of the domain objects that I actually do care about. "How" a piece of code functions is at best the third, and probably only the fourth most important question (behind "why", "what", and probably "when" if you use any concurrency at all), but Go forces it to be front and center at all times. |
I still don't get what your problem is. It seems what you want is to just take an io.Reader and pass that to bufio.NewScanner, solving your problem and letting your caller figure out what Reader to pass you? I mean, to me, this seems to be a solved problem and exactly one of Go's major strengths.
> it makes it difficult to decouple modules since code often has to be at least somewhat aware of quite a few implementation details of a library in order to use it correctly.
You still haven't described a single piece of your code that requires, in any way to know any implementation details of any of the libraries you are using. Like, you don't have to care how os.File is implemented, it just gives you a Read method that you can use to read from it, just like a thousand other Readers. And then you can use that in a bufio.Scanner to read lines (words, whatever tokens), without that having to care in any way about how the Read method is implemented. You want to scan lines from a byte-slice, use bytes.Reader, that's it's sole purpose and your scanning code does not have to care what Reader it gets passed.
Like, I seriously don't understand your problem here. It would seem to me, what you are describing is exactly how Go works.
> "How" a piece of code functions is at best the third, and probably only the fourth most important question (behind "why", "what", and probably "when" if you use any concurrency at all), but Go forces it to be front and center at all times.
Sure, I agree that Go does not encourage you to build deep abstractions. But I fundamentally disagree that you have to know any implementation details - anymore than any other language. Yeah, the type system doesn't lend itself to build extra abstractions, but "having to care about implementation details" just is not one of the symptoms of that o.O