-
Notifications
You must be signed in to change notification settings - Fork 51
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
Add foldlWhile to List.Lazy #156
base: master
Are you sure you want to change the base?
Conversation
Any thoughts on this one @garyb? |
It seems sensible enough to me! I don't really have much of a personal opinion about the @hdgarrood / @natefaubion any thoughts? |
Yeah, seems fine! No strong opinions either way from me. I don't think I've ever used lazy lists in PureScript 😮 |
Is there a reason to not also add such a function for |
Also, isn't this foldl with the arguments flipped? |
src/Data/List/Lazy.purs
Outdated
go (Cons x xs') = | ||
case f x acc of | ||
Left acc' -> acc' | ||
Right acc' -> foldrWhile f acc' xs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you intend to use go
here? Otherwise there's no reason for the binding, you can just case on step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come to think of it, I think using go
here is probably sensible as we can get TCO without having to thread f
through for each iteration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't intend to use go
. The recursive call needs to call step
again, so it needs to go back in through the top. I'm happy to remove go completely, though the extraction makes it a bit easier for me to read the implementation (otherwise you'd have nested case
statements).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be set up in such a way that TCO fires or it will overflow for long lists in the case where the function keeps returning Right for long enough. I’d suggest:
foldlWhile f = go
where
go acc xs =
case step xs of
Nil ->
acc
Cons x ys ->
case f acc x of
Left acc’ -> acc’
Right acc’ -> go acc’ ys
Using a binding for go
like this means that we don’t need to thread f
back through each time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hdgarrood for my edification, why wasn't the initial implementation tail recursive? Is it the mutually recursive call from go
back to foldlWhile
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your implementation could also have been described as tail-recursive, although I was concerned that introducing the go
binding and then recursing with a call to the outer binding would mean it was tail-recursive in a way that the compiler might not be able to recognise and optimise, if that makes sense?
Oh yeah of course, this is folding from the left, so I guess it should be called |
I think we should rename it, and flip the arguments back then. |
|
@hdgarrood my understanding was that folds over lazy lists were almost by definition folds from the right. I'm no expert, though. I'm happy to do any of the following:
Let me know. |
@drewolson What determines left or right fold is just associativity of the operations, and this is left associative. For it to be right associative you would need to pass in the accumulator for the entire tail to the folding function. A lazy I would like both of those tasks to be done: changing the name/signature, and adding an implementation for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming to foldlWhile and flipping the function argument sounds good, yes please. Also could you please add a test where the function keeps returning Right for 100,000 iterations before the first Left, to ensure that we have stack safety?
@hdgarrood @natefaubion I believe I've addressed all concerns. Thanks for all the feedback! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
I'd prefer to keep the internal |
@hdgarrood I actually found the indirection of introducing |
Any further thoughts on this? If it isn't something that folks are interested in, I'm happy to close the PR. |
It's not that we don't want it, it's just that we're spread fairly thin. I'm more focused on the upcoming 0.12.4 compiler release right now. |
@hdgarrood totally understood. No rush from my end, just didn't want to leave it hanging around if it was cruft. Appreciate the response! |
I'm not sure if this is something you'd like to add, but I've found this construct useful in other languages that support explicit lazy lists.
For example, both rust and elixir support something like this function.