A file descriptor is equivalent to the Object universal base class in many OO languages [1] and represents an handle to an OS resource. Now in UNIX you can have anonymous [2] resources, but in the Plan9 model most resources have a name and you can get an handle to it via open(<resource-name>).
Hence open is not part of your UniversalInterface, but it is a way to obtain a reference to it. It seems reasonable to have a generic way to dispose of an UniversalInterface (hence close). read/write are simply a generic ways to send and receive messages from UniversalInterface, not unlike a dynamically typed object. Ideally you would do a checked down cast to your actual interface [3], but this was designed to work with C so you have to make do.
[1] I don't subscribe to the Everything is an Object in the OO sense, but having a common base class for most OS resources seem a reasonable solution.
[2] or at the very least there isn't always a cross-resource-type namespace.
[3] Not unlike COM QueryInterface, and in fact UniversalInterface is equivalent to IUnknown
Hence open is not part of your UniversalInterface, but it is a way to obtain a reference to it. It seems reasonable to have a generic way to dispose of an UniversalInterface (hence close). read/write are simply a generic ways to send and receive messages from UniversalInterface, not unlike a dynamically typed object. Ideally you would do a checked down cast to your actual interface [3], but this was designed to work with C so you have to make do.
[1] I don't subscribe to the Everything is an Object in the OO sense, but having a common base class for most OS resources seem a reasonable solution.
[2] or at the very least there isn't always a cross-resource-type namespace.
[3] Not unlike COM QueryInterface, and in fact UniversalInterface is equivalent to IUnknown