wryun / es-shell

es: a shell with higher-order functions
http://wryun.github.io/es-shell/
Other
313 stars 26 forks source link

Why is $result not exposed by default like other shells' $status and $? #33

Closed hyphenrf closed 3 years ago

hyphenrf commented 3 years ago

It may be useful in many applications where you want to find a command's return status after the fact, not while executing it.
usually to get a command's status we wrap it in <={ ... }, but currently I know of no way to get a command's return status after it had executed. exposing $result by not making its scope local to %interactive-loop (and maybe renaming it to $status so that there's less confusion with the result keyword) would allow for that.

https://github.com/wryun/es-shell/blob/fe5e3a35c40d87bc0ef64ba3624767bccc270170/initial.es#L634-L635 https://github.com/wryun/es-shell/blob/fe5e3a35c40d87bc0ef64ba3624767bccc270170/initial.es#L652-L663

mwgamera commented 3 years ago

I don't know what is the original reason, but it would increase pollution of the global namespace for no good reason and make it harder to opt-out of such behaviour. Currently you can make it behave the way you want simply by redefining %dispatch hook in .esrc or your script so I think this is a better default.

let (dispatch = $fn-%dispatch)
fn %dispatch { status = <={$dispatch $*} }
hyphenrf commented 3 years ago

I'm not sure exactly what's happening, but it seems that because %dispatch is dynamically bound, the $fn-%dispatch we have on .esrc is not the same one in the interactive shell, which seems to break this solution down.

when I try to echo $fn-%dispatch within or outside of .esrc, I get an empty definition, and when I try to print $status on a new interactive shell, I get the return value of the last definition inside .esrc and it doesn't change on every new "dispatch" which made me make those guesses.

mwgamera commented 3 years ago

Ah, you're right. The above would work in a script but %dispatch is reset at the start of input, which is later than I thought and after the .esrc finishes running. (It is also set separately for every . sourced file to one of those according to the flags given.) So basically one would have to modify instead the functions it gets reset to.

fn %eval-noprint { status = <=$* }
fn %eval-print { echo $* >[1=2]; status = <=$* }

This should do the trick, but admittedly is less pretty than I hoped it be.

hyphenrf commented 3 years ago

that did the trick :)

it's also worth noting that $status is, with this solution, not reset between different evaluation contexts, probably because all vars that aren't let-scoped are exported by default(?)

so for example if I redefine %eval-* in the middle of my .esrc, then I open a new interactive shell, my $status variable will contain the last return value of the last expression in .estc. similarly, my $status carries over to the first expression in any script I run.

I think redefining the loop prevents that? if only because the loop is run on every new session separately and at the head of that function, status is zeroed.

hyphenrf commented 3 years ago

by that I mean:

let ( loop = $fn-%interactive-loop )
fn %interactive-loop { status = 0; $loop }
let ( loop = $fn-%batch-loop )
fn %batch-loop { status = 0; $loop }
mwgamera commented 3 years ago

Yes, that should do it, and putting it in .esrc should work (you could of course put the whole loop modified as you initially proposed there too).

Dynamically scoped variables are indeed exported by default and I suppose it would be good to add noexport = $noexport status to prevent that, but this affects only places where it's serialized to the environment. Values of global variables are always preserved between different contexts (including through fork).

hyphenrf commented 3 years ago

Thank you. until a modification like this is considered as the default, I feel like it's safe to close this issue for now.