[swift-evolution] Passing an optional first argument to sequence(first:next:)

Kevin Ballard kevin at sb.org
Fri Aug 19 12:48:54 CDT 2016


AFAIK this issue has never been discussed with sequence(first:next:)
before. It certainly wasn't brought up during review.

As for my opinion, I'm really not sure. I was going to point out that
right now sequence(first:next:) guarantees that the first element of the
resulting sequence is the value provided as "first", but it occurs to me
that if you treat the nil result from next() as an element, then this
still holds true. So I guess my biggest worry is this change will make
it harder to use sequence(first:next:) to produce sequences of optional
values. So I guess I'm ambivalent, and would prefer to defer to the
wisdom of the Swift core team on this matter.

That said, didn't the deadline for source-breaking changes already
come and go?

-Kevin Ballard

On Fri, Aug 19, 2016, at 10:37 AM, Max Moiseev wrote:
> + Erica, Kevin, as the authors of the original proposal.
>
> Do you remember the problem of non-emptiness being discussed before?
> And if not, what’s your opinion on the proposed change?
>
> Thanks,
> Max
>
>> On Aug 19, 2016, at 7:53 AM, Tim Vermeulen <tvermeulen at me.com> wrote:
>>
>> Hi Max, thanks for having a look.
>>
>> A big part of why I’m not really happy with the current
>> implementation is that the function always produces a nonempty
>> sequence, though the compiler doesn’t know it. `sequence(first:
>> first, next: next).last` returns an optional, even though it can’t
>> possibly be nil. The same goes for something like `sequence(first: 5,
>> next: { $0 * 3 }).first(where: { $0 > 1000 })`, because the sequence
>> is infinite, which means `first(while:)` will either keep running
>> forever, or return a non-optional.
>>
>> Ideally, we’d have three types of sequences, with three corresponding
>> `sequence(first:next:)` functions:
>>
>> func sequence<T>(first: T?, next: (T) -> T?) — returns any sequence
>> func sequence<T>(first: T,  next: (T) -> T?) — returns a nonempty
>> sequence
>> func sequence<T>(first: T,  next: (T) -> T)  — returns an infinite
>> sequence
>>
>> Default implementations for methods on sequences would either return
>> optionals or non-optionals depending on their emptiness/finiteness.
>> We just have the first kind of sequence right now, so in that regard
>> it would make sense to also give `sequence(first:next)` the
>> corresponding signature.  Later, when the language / standard library
>> supports the other two kinds of sequences (if that ever happens), the
>> other versions could be added.
>>
>> Another reason that makes me think that the version that accepts an
>> optional `first` argument is more natural, is the fact that the
>> function body doesn’t need to be changed at all. It supports optional
>> seeds by design; only the signature prevents it.
>>
>> I know these arguments might not be very convincing, but I feel like
>> Swift misses an opportunity if it unnecessarily constrains the
>> `first` parameter to be non-optional. The `.lazy.flatMap({ $0 })`
>> alternative that you pointed out does work, but it makes everything
>> very unreadable: not just the `.lazy.flatMap({ $0 })` part, but also
>> the body of the `next` parameter because you’re now dealing with
>> optionals (i.e. you have to `flatMap` over the closure argument). The
>> best solution I’ve come up with is to copy the `sequence(first:next)`
>> implementation from the source code and change the signature. :-/
>>
>> `sequence(state:next:)` isn’t very appropriate for this task either,
>> because naive usage with an optional seed has the downside of being
>> unnecessarily eager just like a naive `sequence(first:next)`
>> implementation (as described in a comment in the source code).
>>
>>> On 19 Aug 2016, at 00:18, Max Moiseev <moiseev at apple.com> wrote:
>>>
>>> Hi Tim,
>>>
>>> Thanks for bringing this up.
>>> Here are my thoughts on the change you’re proposing.
>>>
>>> func sequence<T>(first: T, next: (T) -> T?) ->
>>> UnfoldFirstSequence<T>
>>>
>>> To me the type of the function as it is tells a clear story of
>>> what’s going to happen: take the `first`, make it a head of the
>>> resulting sequence, and then try to produce the tail by a series of
>>> applications of `next`. The only thing that controls when the
>>> sequence generation terminates is the result of `next`.
>>>
>>> If we change the type of `first` to an Optional<T>, it would make
>>> the termination condition non-trivial. After all, the only thing it
>>> would do is try to unwrap the `first`, before doing what it needs
>>> to, but we already have a `map` for that. One should be able to
>>> simply do the `first.map { sequence(first: $0, next: next) } ?? []`
>>> but that won’t work with the types very well, unfortunately.
>>>
>>> As an alternative, `let first: Int? = ...; sequence(first: first,
>>> next: next).flatMap({$0})` (or even `.lazy.flatMap({$0})`) will do
>>> the right thing without making an API more complex.
>>>
>>> I see the point of `sequence(first:next:)` to be precisely the
>>> "generate the non-empty sequence using a seed and a simple
>>> producer", for anything more than that, there is
>>> `sequence(state:next:)`.
>>>
>>> What do you think?
>>>
>>> Max
>>>
>>>> On Aug 14, 2016, at 4:27 PM, Tim Vermeulen via swift-evolution <swift-
>>>> evolution at swift.org> wrote:
>>>>
>>>> sequence(first:next:) takes a non-optional first argument. Is there
>>>> a reason for that? sequence(state:next:) allows empty sequences,
>>>> and I don’t see why sequence(first:next:) shouldn’t. The fix would
>>>> be to simply add the `?` in the function signature; no other
>>>> changes are required to make it work.
>>>>
>>>> I considered just filing a bug report, but since this is a change
>>>> of the public API...
>>>> _______________________________________________
>>>> 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/20160819/1aba6d3a/attachment.html>


More information about the swift-evolution mailing list