[swift-evolution] [Accepted with modifications] SE-0045: Add scan, prefix(while:), drop(while:), and unfold to the stdlib

Kevin Ballard kevin at sb.org
Fri May 6 21:15:59 CDT 2016

On Fri, May 6, 2016, at 06:15 PM, Dave Abrahams via swift-evolution wrote:
> on Fri May 06 2016, Matthew Johnson <swift-evolution at swift.org> wrote:
> >> On May 6, 2016, at 7:30 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> >> 
> >> 
> >> on Fri May 06 2016, Cole Campbell <swift-evolution at swift.org> wrote:
> >> 
> >>> I don't know if it's considered too late at this point to rename 'reduce', but
> >>> I'll add an enthusiastic +1 to renaming it to 'fold' and adding 'unfold'. 'Fold'
> >>> is just as obvious a name as 'reduce', IMO (actually I even prefer
> >>> it). 
> >> 
> >> It's not, IMO.  “Reduce” was chosen deliberately over “fold” because we
> >> think it is more in “common currency” among programmers, in no small
> >> part to the notice that MapReduce has gotten.
> >
> > I was guessing this was the rationale.  But if it’s not too late and
> > “fold” solves the “unfold” naming problem maybe we that balances out
> > the equation.
> Personally, I'm not confident “unfold” would be considered to meet the
> utility bar, even if we changed the name of “reduce,” FWIW.

As I said to Chris in an off-list email, unfold() was actually the function that I was the most excited about in the proposal, because it allows for a very elegant solution to a variety of looping needs, especially now that we've gotten rid of the C-style for loops. One example of its use is something like

  // Iterate over self.view and all its ancestor views.
  for view in unfold(self.view, applying: { $0.superview }) {
      // ...

In fact, just today I hit this exact issue and wished I had unfold(). This same pattern works for any case where an object has a property of the same type and you want to walk the entire chain, such as UIViewController.presentingViewController, or NSError.userInfo[NSUnderlyingErrorKey].

It also integrates very nicely with the other sequence methods, including drop(while:) and prefix(while:). For example:

  // Iterate over someView and all its ancestors, stopping at self.view
  for view in unfold(someView, applying: { $0.superview }).prefix(while: { $0 !== self.view }) {
      // ...

Incidentally, I tried writing up an example of using the stateful version to reimplement zip(), but it ended up being a good argument in favor of the suggestion someone (maybe you?) had for turning the state into an inout parameter.

-Kevin Ballard

More information about the swift-evolution mailing list