bytesRead = read(fd, buf, BUF_SIZE - 1)
You read the from the file descriptor into the buffer. Just like in Go you read from a file into a byte array. Neither the file descriptor nor the file is conceptualized as a tool for reading. The file descriptor is merely an abstraction of the file, which extends the concept of "file" to include pipes, sockets, and other io.In this sense, the GP is right - to respect OS terminology, in Go we should write
fileReaderWriter := os.Open(path, "rw")
Since os.Open doesn't return a file handle/descriptor, but an object capable of reading or writing to a file. This is different from C, where open() does return a FILE*, which is just a handle to a file which you explicitly pass to read() or write() or mmap() etc.The model is similar with Go's interfaces. You can use a FileReader to read from a file on disk and give you the data. You didn't read the data yourself, the Reader read it for you and put it in a []byte.
Interfaces are always named for what purpose they serve to others, not for what they do internally.
I do agree that the File name is misleading for what os.Open actually returns. Java's FileStream name seems to capture the concept more clearly to me. However, I think this is am intentional choice on the part of Go designers, who are somewhat allergic to object-oriented design. This is the same reason they don't want the receiver of a method to be named "this", even though that is exactly what the code models.
Basically, the Go designers want to pretend that Go struts are not objects, they are only data, and that methods on that data are kinda just free-floating functions with some extra syntax sugar. Of course, that is not what Go actually does - a Go struct instance with methods and private fields is exactly equivalent to a Java Object instance and completely different from a C struct instance. But they don't like that framing.
You'll see this all over the Go stdlib - struct are named after the data they represent, but then they also implement interfaces named for the purpose they serve to others.
So what is an instance of the struct named File? Go has chosen a dualistic perspective, like with the particle/wave dualism in physics. In some experiments, a File is just a representation of an open file, that you pass to other methods to do something with (e.g. ioutil.ReadFile(file)). In other experiments, it is an object which can read data from the OS and return it to you (e.g. file.Read(), or ioutil.ReadAll()).
> Go has chosen a dualistic perspective, like with the particle/wave dualism in physics.
This summarizes my complaint. It's not consistent. It forces me to switch between perspectives and keep two contradictory models in my head. As I said, it's not the worst thing -- I can deal with it. But I consider it a kind of conceptual wart in the design, or at least of the naming conventions and idioms.
A final pedantic point. I find your interpretation of "ebook reader" interesting, and grant that it is a valid one, and fits with the Go "thing as tool" perspective. However, I don't believe it is the perspective most people use in everyday life, and I think the dictionary definition makes this clear:
e-reader (noun) -- a portable electronic device used for reading books and other text materials that are in digital form.
That is, and e-reader is the thing, the object, the book-substitute that you read.
From: https://www.dictionary.com/browse/ebook%20reader
EDIT: on 2nd thought, maybe that definition does back up your interpretation. "device used for reading books..." I still feel like in practice it is modeled in people's minds just like a "different kind of book" but maybe that is just me, or just some people....
EDIT 2:
> In other experiments, it is an object which can read data from the OS and return it to you (e.g. file.Read()...)
My other objection here (as I pointed out elsewhere) is that if I am supposed to take the "interface as tool" perspective when interpreting the "Reader.Read()" method, then the variable "file" is misnamed. I read a file, I don't use a file as a reader to read a file.
Since I can only name a variable according to one perspective, the name will always feel wrong when I switch to the other.
Your ebook reader it the thing being read, not the thing that reads. Glasses for reading are also called readers, and they are the tool helping you read. Here, to your point, we see "language being complicated" -- the same convention being used for opposite things. But this is a tangential point leading us astray.
I thought about this some more in the shower, and I think I can distill my objection more clearly.
Methods on objects act on those objects. That is:
is equivalent, in English, to performing the action "do" on "object". To take a random example from Effective Go: We "Stat()" the file (get statistics about it) and we "Close()" the file. This pattern is well-established in both Go and nearly every language with objects. Instance methods are actions performed on their object.Now of course "File" also implements "Reader". Yet when I "f.Read()" the situation is exactly the same as in "Stat" and "Close". I am reading the file.
So where is the "Reader" now? Well the Reader is also the file. In the conceptual model, there is no other object around. The file is both the Reader and the object of reading. And that's my problem. The "reader as tool" is inconsistent with "methods act on objects".
In the tool model you've been arguing for, I would have to write something like:
The redundancy of that aside, this is not typically what you see with Go code (though you sometimes do). You are more likely to see stuff like "f.Read()"As I said, you can argue that Go's "File" object is not meant to represent a file, but instead an abstract tool: a thing which can Read and Stat and Close files.
But that's not what the documentation says: "File represents an open file descriptor."
That is, in the intended conceptual model, the "f" in "f.Read()" is a file. And we are reading it. And that is at odds with it being a "Reader" -- a thing that reads.