A couple of reactions:
-
Using unstructured concurrency with
Task {…}
, without handling cancellation is a bit of an anti-pattern. [I wrote a long and tedious answer explaining how to handle this properly, but in retrospect, that was tangential to the real question here, so I removed that reply.] -
Having a sequence that yielded both the output and the next prompt is also a bit of an anti-pattern. If you really did that, I’d yield an enumeration with associated values for either the prompt string or the repl output string, so the consumer could differentiate between them.
But I would contend that one really does not want a single sequence that intermingles the UI (the prompt) with the output strings. (In your case, your consumer is just printing these strings, so it didn’t matter too much, but if it was doing anything else, this process of sending UI prompt and REPL output both as simple strings would make it very difficult to differentiate between the two.)
Personally, this doesn’t strike me as a great use-case for the AsyncSequence
from another AsyncSequence
. I would just iterate through the sequence:
printPrompt()
for try await input in FileHandle.standardInput.bytes.lines {
let output = try await replOutput(for: input)
print(output)
printPrompt()
}
In answer to the question about how to create an AsyncSequence
from another AsyncSequence
, as you noted, one would generally just map
. For example:
let outputSequence = FileHandle.standardInput.bytes.lines.map {
try await replOutput(for: $0)
}
printPrompt()
for try await output in outputSequence {
print(output)
printPrompt()
}
For what it’s worth, that document you shared with us shows an example of runPrompt
and run
, and the simple and natural Swift equivalents might be:
private func runPrompt() async throws {
var iterator = FileHandle.standardInput.bytes.lines.makeAsyncIterator()
while true {
print("> ")
guard let line = try await iterator.next() else { break }
run(line)
}
}
private func run(_ source: String) {
let scanner = Scanner(source)
let tokens = scanner.scanTokens()
// For now, just print the tokens.
for token in tokens {
print(token)
}
}