Let's assume I have a protocol Parser, defined as follows:
protocol Parser {
associatedtype Element
associatedtype Stream
func parse(_ stream: Stream) -> (Element, Stream)?
}
Now let's conform to this protocol with the following struct:
struct CharacterParser: Parser {
let character: Character
func parse(_ stream: String) -> (Character, String)? {
guard stream.first == character
else { return nil }
return (character, String(stream.dropFirst()))
}
}
Now I we can write a neat extension to Character to create character parsers:
extension Character {
var parser: CharacterParser { return CharacterParser(character: self) }
}
let p = Character("a").parser
print(p.parse("abc"))
// Prints `Optional(("a", "bc"))`
Now let's say I want to hide the specifics of the parser's implementation and use the new opaque types from Swift 5.1. The compiler will let me write the following:
@available(OSX 10.15.0, *)
extension Character {
var parser: some Parser { return CharacterParser(character: self) }
}
So far so good. But now there's no way to call parse(:), as it seems the compiler is no longer able to resolve the type of the argument I'm supposed to provide. In other words, the following won't compile:
let p = Character("a").parser
print(p.parse("abc"))
// error: Cannot invoke 'parse' with an argument list of type '(String)'
The only solution I found was to define a "more specific" protocol that inherits from Parser, e.g. StringParser, that sets the associated type in a same-type constraint. Unfortunately, I do not particularly like this approach, as I feel it would not scale well if I were to define other methods returning Parser instances with more elaborate type constraints. In other words, I would trade exposing specific types (e.g. SomeSpecificParserType) with exposing specific protocols (e.g. SomeSpecificParserProtocol), whereas I would like to stay at a higher abstraction level, ideally to only deal with some Parser return types.
Is there any way I could provide additional information to specify that the associated type of the type I return from the property is String solely on the property definition, so that Swift may later infer the concrete type of p.parse?