[swift-evolution] Proposal: Add scan, takeWhile, dropWhile, and iterate to the stdlib

Kevin Ballard kevin at sb.org
Mon Jan 11 00:28:08 CST 2016


Sounds like you want usage examples, then. Are the ones I gave useful?

-Kevin Ballard

On Sun, Jan 10, 2016, at 10:26 PM, Seth Friedman wrote:
> I wasn't suggesting you should convince the reviewers of the merits of
> functional style programming. I'm coming at it from the perspective of
> "Hey I'm not very familiar with functional style. What cool new things
> could these functions help me do if they're added to the stdlib?".
>
> Not all of your reviewers are necessarily going to be familiar with
> these functions from other languages; I simply think more detail would
> make the proposal more compelling. Ultimately up to you.
>
> Thanks, Seth On Sun, Jan 10, 2016 at 10:20 PM Kevin Ballard
> <kevin at sb.org> wrote:
>> When the proposal is "we have a bunch of functions that match
>> functions used in other languages, lets add a few more from the same
>> category of functions that we missed", does there really need to be
>> much explanation beyond "they're useful in the other languages that
>> have them, they'd be useful for the same reasons in Swift"?
>>
>> If requested, I can provide examples of usage. But if you're not
>> already sold on the benefits of working with sequences in a
>> functional manner, it's out of scope of the proposal to convince you
>> of the merits of that style of programming. And if you are already
>> sold on the benefits of doing so, then adding these functions
>> shouldn't need much explanation.
>>
>> Here's a few toy examples, if it helps:
>>
>> // list of all powers of 2 below some limit iterate(1, apply: { $0 *
>> 2 }).takeWhile({ $0 < limit })
>>
>> // first "word" of a string, skipping whitespace let cs =
>> NSCharacterSet.whitespaceCharacterSet()
>> String(str.unicodeScalars.skipWhile({
>> cs.longCharacterIsMember($0.value) })
>> .takeWhile({ !cs.longCharacterIsMember($0.value) }))
>>
>> // running total of an array of numbers numbers.scan(, combine:
>> +).dropFirst()
>>
>> // infinite fibonacci sequence iterate((,1), apply: { ($1, $0+$1)
>> }).lazy.map({$1})
>>
>> -Kevin Ballard
>>
>> On Sun, Jan 10, 2016, at 09:55 PM, Seth Friedman wrote:
>>> To clear my thoughts up a bit, that wasn't an "I'm too lazy to
>>> Google what these functions normally do" comment, but rather an "I
>>> believe proposals should provide as much context as possible about
>>> what you'd like to add along with the benefits of doing so" comment.
>>> On Sun, Jan 10, 2016 at 9:48 PM Seth Friedman <sethfri at gmail.com>
>>> wrote:
>>>> I'm not familiar with any of the functions listed and would love to
>>>> see more about them and their usefulness to Swift as part of the
>>>> proposal.
>>>>
>>>> Thanks!
>>>>
>>>> Seth On Sat, Jan 9, 2016 at 5:30 PM Kevin Ballard via swift-
>>>> evolution <swift-evolution at swift.org> wrote:
>>>>> Proposal PR submitted as
>>>>> https://github.com/apple/swift-evolution/pull/95
>>>>>
>>>>> -Kevin Ballard
>>>>>
>>>>> On Mon, Dec 28, 2015, at 03:59 PM, Kevin Ballard wrote:
>>>>> > ## Introduction
>>>>> >
>>>>> > Add a few more functional sequence utilities to the standard
>>>>> > library.
>>>>> >
>>>>> > ## Motivation
>>>>> >
>>>>> > We have map, filter, and reduce, but we're missing a bunch of
>>>>> > useful utilities like scan, iterate, takeWhile, and dropWhile.
>>>>> > Interestingly, the stdlib includes an implementation of scan in
>>>>> > the doc comment for LazySequenceType, it just doesn't actually
>>>>> > provide it as API.
>>>>> >
>>>>> > ## Proposed solution
>>>>> >
>>>>> > We extend SequenceType with 3 new methods scan, takeWhile, and
>>>>> > dropWhile. We also add a single global function iterate.
>>>>> >
>>>>> > ## Detailed design
>>>>> >
>>>>> > We add the following extension to SequenceType:
>>>>> >
>>>>> > extension SequenceType {     func scan<T>(initial: T, @noescape
>>>>> > combine: (T, Self.Generator.Element) throws -> T) rethrows ->
>>>>> > [T]     func dropWhile(@noescape dropElement:
>>>>> > (Self.Generator.Element) throws -> Bool) rethrows ->
>>>>> > [Self.Generator.Element]     func takeWhile(@noescape
>>>>> > takeElement: (Self.Generator.Element) throws -> Bool) rethrows
>>>>> > -> [Self.Generator.Element] }
>>>>> >
>>>>> > These all take functions, so to follow convention they're
>>>>> > @noescape and return arrays. We also provide an extension of
>>>>> > CollectionType that overrides a couple of these methods:
>>>>> >
>>>>> > extension CollectionType {     func dropWhile(@noescape
>>>>> > dropElement: (Self.Generator.Element) throws -> Bool) rethrows
>>>>> > -> Self.SubSequence     func takeWhile(@noescape takeElement:
>>>>> > (Self.Generator.Element) throws -> Bool) rethrows ->
>>>>> > Self.SubSequence }
>>>>> >
>>>>> > We also provide lazy versions:
>>>>> >
>>>>> > extension LazySequenceType {     func scan<T>(initial: T,
>>>>> > combine: (T, Self.Generator.Element) -> T) ->
>>>>> > LazyScanSequence<Self.Elements, T>     func
>>>>> > dropWhile(dropElement: (Self.Generator.Element) -> Bool) ->
>>>>> > LazyDropWhileSequence<Self.Elements>     func
>>>>> > takeWhile(takeElement: (Self.Generator.Element) -> Bool) ->
>>>>> > LazyTakeWhileSequence<Self.Elements> }
>>>>> >
>>>>> > extension LazyCollectionType {     func dropWhile(dropElement:
>>>>> > (Self.Generator.Element) -> Bool) ->
>>>>> > LazyDropWhileCollection<Self.Elements>     func
>>>>> > takeWhile(takeElement: (Self.Generator.Element) -> Bool) ->
>>>>> > LazyTakeWhileCollection<Self.Elements> }
>>>>> >
>>>>> > No collection variant of scan is provided because that would
>>>>> > require storing the last value in the index itself, which would
>>>>> > cause problems if the combine function isn't pure.
>>>>> >
>>>>> > LazyDropWhileCollection would behave similarly to
>>>>> > LazyFilterCollection in that it runs the predicate against the
>>>>> > elements to drop when accessing startIndex; unlike
>>>>> > LazyFilterCollection, because there's nothing else to skip after
>>>>> > that point, the index itself can actually be Self.Elements.Index
>>>>> > (just like a slice). LazyTakeWhileCollection also runs the
>>>>> > predicate against the first element when accessing startIndex,
>>>>> > but it does need a unique index type (because endIndex has to be
>>>>> > some sentinel value, as it doesn't know where the end is until
>>>>> > you reach that point; this index type would therefore only
>>>>> > conform to ForwardIndexType).
>>>>> >
>>>>> > And finally, we provide a global function
>>>>> >
>>>>> > func iterate<T>(initial: T, _ f: T -> T) -> IterateSequence<T>
>>>>> >
>>>>> > This function is inherently lazy and yields an infinite list of
>>>>> > nested applications of the function, so iterate(x, f) yields a
>>>>> > sequence like [x, f(x), f(f(x)), ...].
>>>>> >
>>>>> > -Kevin Ballard
>>>>> _______________________________________________
>>>>> swift-evolution mailing list swift-evolution at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160110/572c1d44/attachment.html>


More information about the swift-evolution mailing list