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 <[email protected]> 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
>> <[email protected] <mailto:[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