-
Notifications
You must be signed in to change notification settings - Fork 9
discussion on fs module API design
I'd like to discuss on API design of the fs module.
-
fs module to be an object or a plain module
- For example, fs:open(...) or fs.open(...)
- At first, I prefer the latter. If we have multiple filesystem implementations (for example, normal filesystem and some distributed file systems), fs being objects may be useful. However, fs:open() seems unnatural to me.
- If fs is an object, we can call fs:on_error() when an error occurs (it's somewhat the same style as tcp_server:on_close()). If we create one fs object for a series of fs operations and another fs for another series, maybe error handling becomes easier (Examples are needed here). It may be a sort of async version of try catch. I'm not sure yet.
- -> @kristate and @hnakamur decided fs is a plain module. There is no methods fs:do_something(), all function are called with fs.do_something().
-
Sync and async versions of the function has different names or the same name.
- For example, fs.open(...) and fs.openSync(...) (different names) or fs.open(..., cb) or fs.open(...) (same name).
- I prefer the latter. This is libuv's style. And implementation of lev becomes simpler this way.
- I suppose naming rule for sync version to be openSync is introduced because it is easier for us to distinguish sync calls and async calls. If this is important, we would be better to have different names.
- -> @kristate and @hnakamur decided the latter.
-
error to be a string or an object.
- For example, "permission denied" (a string) or { code = EACCES, message = "permission denied" } (an object)
- I prefer the latter. We need both code and message. Especially some error codes has no messages. https://github.com/joyent/libuv/blob/master/include/uv.h#L67-126
- "Programming in Lua 2nd Ed" says "Despite its name, the error message does not have to be a string". So I'm confident here.
- -> @kristate and @hnakamur agreed errors should be objects.
-
error to be the first argument or the last argument or another pattern.
- For example, callback becomes function(err, stream, nread, buf) or function(stream, nread, buf, err).
- I prefer the latter, but i'm not sure it is better. I think the latter is Lua's conventions. When it succeeded, nread and buf are non nil and err is nil. When is failed, read and buf are nil and error is non nil. However, in this example, stream is always non nil (it is needed to calling close(), for example).
- If fs is an object and we use fs:on_error() for error cases, then we don't need to pass error as an argument of callback functions.
- -> @kristate and @hnakamur decided to use function(instance, error, nread, buf). It should be in the order that you would use/check the data. Since fs is a plain module and there is no instance, callback becomes function(error, nread, buf).
@dvv: imho
-
plain module and no more in the core. those who want polymorphism should write their wrappers as external libs
-
agree, but s/openSync/open_sync/g
-
i would do it simple string. just like erlang (which charms me more and more with its solid-rock simplicity) returns tagged tuples kinda (ok, result)/(not_found, error). they never had objects and never missed them :) however, anything non-nil would do for error and should be left up to higher level logic
-
agree
@hnakamur: hi, @dvv, thanks for you comments.
-
agree
-
it seems you misunderstood our intension. there is no open_async(). we use open() for both async and sync operations.
-
You have a point here. I thought, if we use objects, we can later add other attributes like function names. However, this maybe a YAGNI (You Ain't Gonna Need it). Erlang charms me, too. It's very pragmatic and has a lot of mechanisms that actually work well in real environments. However, Lua doesn't have pattern matching for receiving multiple values. So you must write like:
local status, result_or_error = some_function(...)
I don't like the name like result_or_error, but this is the style that pcall uses. In Erlang we can use pattern matching to {true, result} or {false, error}, so variable names are nice. I prefer
local err, result = some_function(...)
because of nice variable names. As for error, if we use strings, we should use error code names like 'ENOENT'.
- agree