halt no longer directly calls std::process::exit#433
Conversation
|
Thanks for your kind words --- I'm always happy about such experience reports! :) Concerning your implementation: How about adding a |
|
Hm, I see then that If this is a concern, we could introduce a new |
|
Regarding adding a new variant to If adding a new variant to impl<V> Exn<'_, V> {
pub fn halt(exit_code: isize) -> Self;
pub fn exit_code(&self) -> Option<isize>;
}The current Does this seem like a decent approach to you? |
Modifies the `halt` builtin to return a kind of `Exn` that, by default, exits the process when unwrapped. This allows embedders (library users) to handle `halt` calls themselves to e.g. return a custom error when running a filter, rather than taking down the whole process.
bf13571 to
268ddd6
Compare
|
I've extended |
The comment that you write about, as you mentioned, is for the I'm already pretty happy with the direction that your PR is taking. Still, this issue inspired me to think about whether this might be a good opportunity to extend this to some kind of effect system. Because there is not only This could be done e.g. by adding This approach might add a new layer of complexity to the jaq core, because every I'm just thinking aloud here. What do you think about the whole idea of generalising this to a (light) output effect system? Because the effect system outlined here handles only the O of I/O --- for the I, the |
|
I've thought about this a bit more: The problem of the effect system I outlined above is that this can only handle effects that never fail; for example, errors from a hypothetic filter I have yet another wild idea: If we consider only I have to think a bit more whether this is all too much voodoo. Perhaps the |
Also update the docs to mention platform-specific limitations on process exit codes.
|
Personally, I think it's better to keep If you do want to go the route of the 0 label meaning the entirety of filter execution, I'd say it's probably worth redefining labels to be In terms of a more general "effect system", Adding some kind of effect payload that returns to a top-level handler, which then needs to feed back a (possible) return value back into the filter runtime, which then needs to be able to resume filter execution (kind of similar to an Most importantly, however, is the fact that the other impure filters aren't surprising (to an embedder), and that the other impure filters can be implemented by an embedder in 3.0.0, but The impure filters - those that "escape the filter context" in some manner - are:
The actual project I was working on when I encountered this currently has a snippet like: let funs = jaq_all::data::funs()
.filter(|fun| fun.0 != "halt")
.chain(std::iter::once(jaq_all::jaq_std::run((
"halt",
jaq_all::jaq_std::v(1),
|mut cv| {
let val = jaq_all::jaq_core::Ctx::<jaq_all::data::DataKind>::pop_var(&mut cv.0);
let exit_code = jaq_all::jaq_std::ValT::as_isize(&val).ok_or_else(|| jaq_all::jaq_core::Error::typ(val, "integer"));
jaq_all::jaq_std::bome(exit_code.and_then(|exit_code| Err(jaq_all::jaq_core::Error::str(format_args!("halt({exit_code})")))))
},
))));Note that this can't terminate filter execution, as that primitive isn't in 3.0.0 - it can either call In short, most other useful potentially-fallible effects (except "suspend execution and re-enter at a later time") can already be implemented by embedders in 3.0.0, with the exception (heh) of there not being an existing primitive for ending filter execution - based on that, I don't think an effect-like system is needed. |
|
I agree with your reasoning, @a-n-d-r-e-w-l. I have also thought about this over the weekend and think that A few more comments for the API:
|
|
re: points 1 (move from re: point 2:
I *think* the easiest solution is just to make Thoughts? Edit on point 1: getting the exit code uses If moving the filter definition to |
…method This removes the call to `std::process::exit` within `jaq-core`, which would not be compatibl with `#![no_std]`. Incidentally, this also makes the web playground print a message with the exit code passed to `halt/1`, if any, as explicitly handling halt/error cases is required to pass typecheck.
As `jaq-core` is fully generic over the value type, this requires moving `i32` extraction into `jaq-std`.
|
Just a little thought on I'll have to think about the other things a bit first. But I think that I like your |
You're right. I thought that
Yes, and there would also be the problem that with this approach, users could not reliably catch errors coming from Conclusion: Let's keep |
| `jq` does not implement `halt($exit_code)`, only `halt`. | ||
| ::: | ||
|
|
||
| Note that not all values for `$exit_code` will work as intended on all platforms: see the |
There was a problem hiding this comment.
This looks like an advanced block to me.
|
OK, I think I'll take this from here. The API is now quite clear to me. I'll polish things to my liking before merging. |
|
Hi @a-n-d-r-e-w-l. I have now finished my first polishing round. In particular, I reintroduced Do you have feedback for the current state of the PR? |
|
Sorry for the delayed response, I just didn't see the comment. Yep, the current state of the PR looks good! |
|
Thanks for your heads up, @a-n-d-r-e-w-l. |
In one of my projects I used
std::process::Commandto shell out tojqfor lots of fairly fiddly JSON processing - switching from "shell out tojq" to "usejaqas a library" saw speedups of something like 200x for my usecase (amazing, thank you for making this!). The only blocking issue I encountered was that some of my jq scripts usedhalt_errorto signal invalid states - when shelling out and checking exit codes it all worked, but switching tojaq-as-a-library meant thathalt_errorin a script brought down the entire process rather than returning some kind of handleable error to the user.This PR modifies the
haltbuiltin to return a kind ofErrorthat contains an exit code and cannot be caught by atryblock.This allows embedders (library users) to handle
haltcalls themselves - as an example, the CLI detects theseErrors and callsstd::process::exitat a more convenient location, preserving the same CLI behaviour, but other embedders can handle the error in different ways depending on their intended use case.Checklist