-
-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
On the type of Luv.Loop.run and "natural APIs" more generally. #54
Comments
Here's an initial "thinking" response. None of it reflects any "final" decisions I am making; instead, I just want to reply with my thoughts until now, such as the considerations that went into making the OCaml API how it is. We can discuss further :)
Would it help to more prominently indicate to readers, that the docs should be used in this way?
This is definitely a real ergonomics issue on the OCaml side. I should probably begin with rewriting the examples not to use
Indeed, but the stated goal is not exactly to provide "more natural APIs" — quoting it that way discards context, and if that context is not discarded, the opposition in goals goes away, too :) As it says, the goal of more natural APIs only applies in places where the libuv API is hampered by C, such as in
I agree with this. Part of the issue is that I originally saw two "major" use cases for Luv:
Since I imagined the library would be mostly used in the implementation of other libraries, I thought it's acceptable (even desirable) to trade polish of the API for direct, predictable correspondence with the behavior of libuv, so that Luv stays out of the way of higher-level implementors as much as possible, except where all likely implementors would probably agree that something should just be taken care of as part of the translation done by Luv. If Luv gets more direct usage, this will probably have to be adjusted in some way. Perhaps a thin wrapper with less sharp corners would need to be written, or there is a compromise on all strange APIs that can be found, that makes Luv directly suitable for both kinds of usage. |
Also
We can just break Luv all the time, it's in 0.x and has few users :)
Don't we need creation functions for most kinds of objects? The choice of name Also, I will add a sentence at |
I added the minimal, initial change of replacing ignore (Luv.Loop.run ()) by ignore (Luv.Loop.run () : bool) in the README and all examples, so that at least it forms a habit that, if practiced, will make partial application less likely. Posted the new docs. |
Thanks! I completely agree with the approach of having Luv be thin low-level bindings that correspond closely with its C counterpart, and building higher-level bindings on top of that. I've even used this approach before myself. I guess my expectations were just skewed a bit because despite being low-level and relatively unpolished, this is still the most polished and well-documented API I'm aware of for what it provides! It might still make sense to make minor alterations like turning the
Definitely. Although I did look at the docs for
To be fair, I did quote it with context too, although that still didn't stop me from interpreting it wider than intended :)
The APIs I've interacted with so far, mostly just Of course arguments to the contrary include consistency across modules and correspondence with the C API, and I think you've already convinced me that it should remain as it is. But if the goal was a more natural API then |
It occurs to me that we probably only need one of those,
Thanks, I added a further commit with a sentence about that.
Indeed, Apart from consistency on the level of aesthetics or reading docs, there is a certain subtype relationship between libuv base handles and |
Yep, like me 😬 So maybe not 😉 I'm integrating with Revery, which has its own app loop and offers no control over it. I have to hook into it with what's essentially a
Cool! We need an API that does globbing as well, or essentially the ability to emulate the fswatch API. So if that aligns with those plans, I'm pretty sure we can offer some manpower to get it going relatively soon.
Yeah that makes complete sense. Thanks for explaining! |
Related question that maybe could result in something being added to the docs: When you are using the default mode, and you get |
Could you create a little program that creates a long-lived connection to some test server, then stops the default loop. Does this cause |
(Or merge a PR to the docs, as you choose! :) ) |
Luv.Loop.run
returns a boolean value whose meaning does not seem to be documented and, judging by the examples, in most cases is not used, but usually justignore
d.I would argue that using
ignore
without any type annotation is very error-prone because it sets the trap for accidental partial application. It should be considered bad style IMO, and therefore should be avoided particularly in examples. I also thinkLuv.Loop.run
exacerbates the likelihood of errors because its arguments are all optional except for the terminatingunit
. It's very easy to specify one or more optional arguments but forget the terminating unit. This leads to partial application, and with the use ofignore
will go entirely unnoticed except for the observation that nothing is happening, which is a symptom that doesn't narrow down the possible causes a whole lot.These issues are also in most cases preventable by providing a better API, for example:
I still don't know what the
bool
means of course, so this might still be way off. My assumption here is that it means there's more work to do, or something along those lines. But if it means something else this API probably doesn't make much sense.Furthermore, it's a stated goal for Luv to provide "more natural APIs":
But it's also stated that Luv is "a fairly thin binding", which to a significant extent opposes the above goal. And there's certainly also a documentation and familiarity benefit of having the API closely mirroring the original.
So there are two parts to this issue I think. One is the more general question of how much deviation from the C API is considered acceptable. Where the line is, or should be, drawn between providing "natural APIs" and being a thin binding. The other is the specific question concerning this particular API, its use cases, the meaning of the boolean and what follows as a natural API
To summarize:
bool
return type ofLuv.Loop.run
is not documented.ignore
.ignore
is error-prone and IMO bad style.PS. I'm just starting to get my feet wet with Luv, and so far it seems very solid and well-documented, although in need of some polish in places. I ask the more general question because this isn't the only part of the API that feels unnatural. The use of
init
everywhere, for example, seems unnecessary in most use cases. Anyway, it's certainly still very usable thanks to the great work you've done on documentation, and I want to emphasize how much I personally appreciate that little detail, as well as thank you for the library as a whole.The text was updated successfully, but these errors were encountered: