Add lazy closure evaluation to default (#14160)#15654
Conversation
default (issue #14160)default (fixes #14160)
|
It's worth noting that even closures can be returned by lazy default -l { {ls} } |
|
I was considering making closure evaluation the default, but it appears there's something relying on default sending a closure object since I got a panic when trying to |
|
wait, you got a panic? that's not good, could you push a second branch to test what's up with that? |
|
That was from a previous attempt a few weeks back so I could be misremembering, and it was just an error in that code. I've fiddled with the current PR implementation to see if I could find it, but everything seems to work, so I think it's a false alarm. As an aside, it does seem like we can enable lazy closure evaluation by default, if nobody would mind a breaking change for user scripts. diff --git a/crates/nu-command/src/filters/default.rs b/crates/nu-command/src/filters/default.rs
index a1ce21148..f02c9e507 100644
--- a/crates/nu-command/src/filters/default.rs
+++ b/crates/nu-command/src/filters/default.rs
@@ -30,14 +30,14 @@ impl Command for Default {
Some('e'),
)
.switch(
- "lazy",
- "if default value is a closure, evaluate it",
- Some('l'),
+ "no-eval",
+ "if default value is a closure, pass it as-is",
+ Some('n'),
)
.switch(
- "lazy-once",
- "evaluate the closure only once, even for lists (no closure input)",
- Some('L'),
+ "eval-once",
+ "evaluate default value closure only once, even for lists (no closure input)",
+ Some('o'),
)
.category(Category::Filters)
}
@@ -54,7 +54,7 @@ impl Command for Default {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let empty = call.has_flag(engine_state, stack, "empty")?;
- let lazy = call.has_flag(engine_state, stack, "lazy")?;
+ let lazy = !call.has_flag(engine_state, stack, "no-eval")?;
let lazy_once = call.has_flag(engine_state, stack, "lazy-once")?;
default(engine_state, stack, call, input, empty, lazy, lazy_once)
} |
|
Doesn't > ls | upsert title {|row| $row.name | str upcase }
╭───┬────────────┬──────┬────────┬────────────┬────────────╮
│ # │ name │ type │ size │ modified │ title │
├───┼────────────┼──────┼────────┼────────────┼────────────┤
│ 0 │ Cargo.toml │ file │ 6.9 kB │ a week ago │ CARGO.TOML │
│ 1 │ LICENSE │ file │ 1.0 kB │ a week ago │ LICENSE │
│ 2 │ README.md │ file │ 289 B │ a week ago │ README.MD │
│ 3 │ src │ dir │ 1.0 kB │ a day ago │ SRC │
│ 4 │ tests │ dir │ 192 B │ a week ago │ TESTS │
╰───┴────────────┴──────┴────────┴────────────┴────────────╯
> [{a:1 b:2} {b:1}] | upsert a {|row| if $in == null { $row.b + 1 } else { $in } }
# or
> [{a:1 b:2} {b:1}] | upsert a {|row| default ($row.b + 1) }
╭───┬───┬───╮
│ # │ a │ b │
├───┼───┼───┤
│ 0 │ 1 │ 2 │
│ 1 │ 2 │ 1 │
╰───┴───┴───╯ |
sounds good to me. I would guess not many people are using a closure lazily, and I think the behavior makes sense. |
For table/record operations, yes. It's basically
|
default (fixes #14160)default (fixes #14160)
|
Passing input to I know it's slightly more verbose but I would rather have > [{a:1 b:2} {b:1}] | default { $in.b + 1 } a
# vs
> [{a:1 b:2} {b:1}] | upsert a {|row| default { $row.b + 1 } }
Also is .. | insert fn { {..} }
.. | update fn { {..} }
.. | default { {..} } fn
# vs
.. | default --no-eval {..} fn |
|
So are you suggesting removing the use case of running For the second point, if we do keep eval-by-default, I don't have a strong opinion one way or another for keeping |
|
I changed the command to always evaluate closure values, following @weirdan and @Bahex's observations that returning a default value closure is possible without If there are no other concerns, I can rewrite the PR description in the initial comment. |
wait, does that mean that |
|
Yes, you're right, lol 🤦. I'll fix that right now. Edit: I really can't see a way to do this for the |
|
For the |
|
Well, I bit the bullet and stopped trying to shove everything in the map function. The result is a large increase in LOC, but the command runs at the same speed for non-closure values ( Summary copied from commit message:
Thanks to everyone for putting up with this for so long. It was much more work than I anticipated or was ready for 😅. I'll try to fix any issues you come across when testing. Edit: One thing I noticed is that if this is added, Edit 2: Did my last update until this is reviewed, to speed up the command by postponing creating the closure object ( Edit 3: I've updated the original PR description with examples as to how the command functions as of this latest commit, if anyone is confused. |
I think the |
Does that imply we want to remove the column argument from |
Do you mean The previous implementation of the PR with
|
|
I've updated the original PR description with examples as to how this PR currently functions. Since there's no closure input currently, and in the spirit of |
Yes, of course. I must have been distracted at the moment.
Ah, ok, sounds good then. |
default (fixes #14160)default (#14160)
132ikl
left a comment
There was a problem hiding this comment.
Thanks for implementing this! It's looking much better 😄
I did notice that this made it incompatible with streaming when using column names, which is different to the previous behavior. We also are streaming in the no column names ListStream branch, so I thought we should try to stream here.
I try to make sure what I'm thinking is actually possible before suggesting it to newer contributors, so I started playing around with it and ended up implementing the whole entire thing before I was able to determine that it is indeed possible. I didn't mean to step on your toes so much in this PR, I don't want you to feel like we don't value your contribution! As you mentioned, this ended up being a surprisingly complicated PR.
I tried to split up my changes into individual GitHub suggestions with explanations so you can understand the changes I made. You should be able to safely apply them as-is. Let me know if you have any questions or anything, and again sorry for accidentally writing so much code on what is ostensibly your PR 😅
Still need to work on porting old mapping code to support lists and records
Thank you @132ikl, it's so much cleaner now 🙏
Co-authored-by: 132ikl <132@ikl.sh>
It's not a problem at all. All of my PRs so far have been things that I wanted to use as a user of Nushell, so I appreciate your help in adding this feature 🙏. I'm definitely learning a lot about Nushell internals, so hopefully I'll be able to tackle other larger issues.
Thanks for commenting your reasoning behind each change, too. There were some problems because a commit changed tests to use a newer rust edition, but it's all been sorted out easily. |
|
thanks so much!! just one last thing, I promise! would you be able to add a couple tests for the new functionality in A test to make sure the evaluation is actually lazy, a test for lazy returning a closure value, and testing closure evaluation with columns and with |
|
looks great! thanks so much for your work on this, a long awaited feature! excited to see future contributions from you 😄 |
This is a breaking change that gave me a startup error based on my Carapace completion closure using a Wrapping it in |
|
Ignore the original message - Bruce's response is spot on.
I think it's going to be difficult to document (or at least for users to understand) why Is it possible to switch the behavior so that:
|
That's ... too magical and has no parallels in nushell, as far as I can tell. |
|
Fair enough - Ignore my late night hallucinations :-) |
|
just remember to get back to this now, thanks for adding the breaking change tag @NotTheDr01ds, I forgot to add that! not sure if you saw, but I did also open a PR to fix the carapace issue: carapace-sh/carapace-bin#2796 |
After nushell/nushell#15654, the direnv integration fails due to default behaving differently when passing a closure.
#15654 (#15806) # Description Adds a temporary workaround to prevent #15654 from being a breaking change when using a closure stored in a variable, and issues a warning. Also has a special case related to carapace-sh/carapace-bin#2796 which suggests re-running `carapace init`   # After Submitting Remove variable name detection after grace period
nushell#15654 (nushell#15806) # Description Adds a temporary workaround to prevent nushell#15654 from being a breaking change when using a closure stored in a variable, and issues a warning. Also has a special case related to carapace-sh/carapace-bin#2796 which suggests re-running `carapace init`   # After Submitting Remove variable name detection after grace period
Description
This PR adds lazy closure evaluation to the
defaultcommand (closes #14160).defaultacts the same as beforeFor example:
User-Facing Changes
default.default { $my_closure }).Tests + Formatting
All tests pass.
After Submitting