4 ms·
There are valid usecases for libraries to take callbacks to parameterize stuff - namely, when the library needs something from the client that the client should
by jstimpfle 5y ago
There are valid usecases for libraries to take callbacks to parameterize stuff - namely, when the library needs something from the client that the client should always be able to provide synchronously, and it would be too much work to create an interface to let the client pass in the information asynchronously.
Example: memory allocation?
But it doesn't work the other way around: The library shouldn't try to restrict the user about all the things it should want to do. Taking dozens of hooks to cover all possible situations will only make the API harder and harder to use.
- aidenn0 5y agoYou may be misunderstanding. These aren't explicit hooks any more than a thrown exception is. Imagine this pseudo python: tryNounwind: Foo() except SomeException as e # This block is run as soon as SomeException is raised, before unwinding the stack There is still an equivalent to try that doesn't unwind the stack. One major use for this is to call dynamic restart points that intermediate functions have provided. A function implementing an RPC shouldn't have to decide how to handle e.g. a network error, but there are several reasonable things that it could do (retry, fallback to a different host, &c.), it can provide those hooks while defaulting to propagating the error up, and since the caller of the function can run its exception handler without unwinding the state, those hooks are accessible to it. It also dovetails nicely with a debugger, because the restarts can be invoked interactively.
- jstimpfle 5y agoNo no, I understand. It is callbacks. Or some fancy syntax that creates and registers a closure implicitly. But technically it's just a callback - the library calls back into the user code, synchronously. The syntax was never the problem, it's more like this syntax sugar (or in the case of LISP, let's call it semantic sugar) is making it way too easy to do something complicated like this. My strong opinion is that this is not a good idea to do. RPC is bad, it's an extremely leaky abstraction (network requests pretending to be synchronous function calls). Maybe that's ok for scripts, or in a compute cluster that can guarantee high availability and low latency, but it's nonsensical for general programming (another reason for slowness and clunkiness of so much software).
- aidenn0 5y ago> No no, I understand. It is callbacks. Or some fancy syntax that creates and registers a closure implicitly. But technically it's just a callback - the library calls back into the user code, synchronously. The syntax was never the problem, it's more like this syntax sugar (or in the case of LISP, let's call it semantic sugar) is making it way too easy to do something complicated like this. My strong opinion is that this is not a good idea to do. This is essentially how traditional exceptions work, it's just that it also happens to include a non-local transfer of control first. Every place an exception is raised is an additional surface to the API. > RPC is bad, it's an extremely leaky abstraction (network requests pretending to be synchronous function calls). Maybe that's ok for scripts, or in a compute cluster that can guarantee high availability and low latency, but it's nonsensical for general programming (another reason for slowness and clunkiness of so much software). Perhaps this is overfocusing on my example? File system access, database requests, memory allocation, are other examples of things that can fail that have multiple plausible ways of handling the failure, and separating the implementation of handing the failure from the choice of how to handle the failure can be useful. [edit] I mostly agree with you on RPC; I picked that example specifically because it has the most non-local exceptional situations of things I could think of on the spot.