What you’re saying makes sense, and I might not have brought this up in the 
first place if `first.map { sequence(first: $0, next: next } ?? []` worked. The 
main annoyance is that the best solution (currently) seems to be to copy the 
source code and make a change.

(cc-ing Jordan Rose because of a related swift-users thread) This might be a 
bit of a stretch, but can’t Swift upcast sequences to AnySequence implicitly, 
like is done with AnyHashable? That would make `first.map { sequence(first: $0, 
next: next } ?? []` instantly valid, I think. There’s also something to be said 
for consistency between type erasers. (I’m not necessarily talking about Swift 
3)

> On 20 Aug 2016, at 02:22, Max Moiseev <mois...@apple.com> wrote:
> 
> 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 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>>> 
>>> On 19 Aug 2016, at 19:48, Kevin Ballard <ke...@sb.org 
>>> <mailto:ke...@sb.org>> 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 <tvermeu...@me.com 
>>>>> <mailto:tvermeu...@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 <mois...@apple.com 
>>>>>> <mailto:mois...@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@swift.org>
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to