0

I'm looking for a method that allows you to react upon the previous output to create a new stream.

It would be similar to scan, but rather than returning an output, it would return another publisher that publishes the next result.

Here's something similar to the scan example:

func next(x: Int) -> AnyPublisher<Int?, Never> {
    return Future { x < 5 ? $0(.success(x + 1)) : $0(.success(nil)) }
}

next(0)
    .fold { x != nil ? next(x) : nil }
    .collect()
// Yields [1, 2, 3, 4]

Something more real:

paginated(0)
    .fold(limit: 100) { $0.index < $0.total ? paginated($0.index + 1) : nil  }
    .collect()
// Yields all pages as an array
Ivorius
  • 356
  • 2
  • 4
  • 13

2 Answers2

1

Sounds like you're looking for an "unfold" operation. Having a function that accepts some state and produces output + next state, it can produce a sequence of outputs from a single state.

Details here: https://nabilhassein.github.io/blog/unfold/

No idea how to do it in Swift though.

urmaul
  • 7,180
  • 1
  • 18
  • 13
  • Oh yeah, you're right. Sounds more like the opposite of a fold. – Ivorius Dec 21 '20 at 14:31
  • Interestingly, Swift seems to have an implementation for it in classic sequences: https://developer.apple.com/documentation/swift/unfoldsequence But as far as I can see, no Combine implementation. – Ivorius Dec 21 '20 at 14:41
0

I ended up implementing something I'm more or less happy with for now myself:

func unfold(limit: Int = -1, _ fun: @escaping (Output) -> AnyPublisher<Output, Failure>?) -> Publishers.FlatMap<AnyPublisher<Output, Failure>, AnyPublisher<Output, Failure>> {
    flatMap { value -> AnyPublisher<Output, Failure> in
        let justPublisher = Just(value).mapError { $0 as! Failure }
        
        guard limit != 0, let publisher = fun(value) else {
            return justPublisher.eraseToAnyPublisher()
        }
        return justPublisher.append(publisher.fold(limit: limit - 1, fun)).eraseToAnyPublisher()
    }
}

I'm very open to suggestions on variant approaches though!

Ivorius
  • 356
  • 2
  • 4
  • 13