Skip to content
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

Deprecate foldRight and all lazy folds #2370

Merged
merged 2 commits into from Apr 9, 2021
Merged

Conversation

1Jajen1
Copy link
Member

@1Jajen1 1Jajen1 commented Apr 6, 2021

FoldRight and similar are useless in most cases in arrow atm.

  • No kinds, without kinds we know that all implementations of foldRight atm are on non-lazy types that call the fold in place
  • A consequence of the above is that all of these folds are inline and can thus short circuit using non-local returns.

A quick overview of all the types of folds present in arrow atm:

  • foldLeft = fold This iterates over the elements in order left to right and accumulates along the way
  • foldRight (without Eval) This iterates over the elements in order right to left. I'd argue that this is hardly ever what anyone wants or needs and writing reverse().fold isn't that annoying or common to warrant a shortcut (especially not one named foldRight)
  • foldRight (with Eval): This returns a lazy computation that is right nested. This means it only ever iterates the list as far as necessary to calculate a result. Thus it is also safe for infinite structures!

Not in arrow as this is almost never useful

  • foldLeft (with Eval) This builds a lazy computation that is left nested, which means to produce the final lazy computation we must traverse the entire list first. Highly inefficient when we want to short circuit or when we actually need every result applied.

So out of the 4 only the strict fold and foldRight with Eval seem useful. fold for obvious reasons, and lazy foldRight because it can short circuit at any point by simply not forcing more of the accumulator.

The stdlib includes the strict left fold and the strict right fold, so why no lazy folds? Well all of the folds inside the stdlib are inline and thus non-local returns can handle the use-case of short-circuiting way better. It is cleaner, faster and easier to understand to return from a local inline fold function than to explicitly not-bind the lazy accumulator.

The other useful part of lazy folds is that their results themselves are lazy. Top level laziness can be achieved by wrapping them in Eval.later { .. } instead.

Here is an example how a lazy right fold can be replaced by a normal fold for exactly the same functionality:

fun <A> List<A>.find(p: Predicat<A>): A? = foldRight(Eval.now(null)) { a, acc -> 
  if (p(a))
    Eval.now(a) // short circuit since we don't evaluate acc anymore
  else
    acc // bind acc and thus force one more step of evaluation
}.value() // evaluate computation
// This now becomes
fun <A> List<A>.find(p: Predicat<A>): A? = fold(null) { acc, a ->
  if (p(a)) return@find a else acc
}
// This can be done without a fold as well, but lets ignore that for the sake of the example.

Copy link
Member

@nomisRev nomisRev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @1Jajen1, and great explentation! 👏 👏 👏

I'd actually like to add this in the docs somewhere since short-circuiting fold instead of traversing the whole fold seems to be a common question in FP.

Copy link
Member

@raulraja raulraja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @1Jajen1 great description! 👏

@rachelcarmena rachelcarmena merged commit f5a7ca6 into main Apr 9, 2021
@rachelcarmena rachelcarmena deleted the jo-deprecate-foldRight branch April 9, 2021 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants