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> 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
> 

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

Reply via email to