Hi Tim,

I still believe that having 2 termination conditions is wrong. But I guess we 
need a tie breaker here, someone with a strong opinion about the problem.
As Kevin mentioned we are very late in the release process, so waiting for 
another opinion for a day or two won’t change anything, really.

Meanwhile, I played a little bit with an idea of making `first.map { 
sequence(first $0, next: next} ?? []` work. 
Turns out, if we add an `ExpressibleByArrayLiteral` protocol conformance to the 
`UnfoldSequence`, this snippet will compile just fine. One downside is that the 
`ExpressibleByArrayLiteral` protocol allows creating non-empty sequences as 
well, which does not make sense for the `UnfoldSequence`.


Max

> On Aug 19, 2016, at 3:48 PM, Tim Vermeulen via swift-evolution 
> <[email protected]> wrote:
> 
>> 
>> On 19 Aug 2016, at 19:48, Kevin Ballard <[email protected] <mailto:[email protected]>> 
>> wrote:
>> 
>> 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.
> 
> I don’t think producing sequences of optional values would really be a 
> problem, because type inference will figure this out based on whether you 
> treat the argument to the `next` closure as an optional or not. And if you 
> only do things in `next` that work both with optionals and non-optionals 
> (very unlikely), you can always manually specify the type of the sequence.
> 
>> 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 <[email protected] 
>>>> <mailto:[email protected]>> 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 <[email protected] 
>>>>> <mailto:[email protected]>> 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 
>>>>>> <[email protected] <mailto:[email protected]>> 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
>>>>>> [email protected] <mailto:[email protected]>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected] <mailto:[email protected]>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to