I'm able to resolve this by using a single declaration of description and using optional binding to check for specialised lexme.
extension Token {
var description: String {
if let _ = self as? Token<Literal> {
return "Token with literal"
} else { return "Regular token" }
}
}
print(Token(lexme: Keyword())) /// Regular token
print(Token(lexme: Literal())) /// Token with literal
Question
If description from the extension with generic type constraint is never used, is there a reason why the compiler does not emit Invalid redeclaration of 'description' error? I believe this could be insidious, given it nudges the programmer to look elsewhere while debugging.
This has come up before. IIRC it will use the specialization when the static type is Token<Literal>, but not in generic functions or when erased to any P or Any. In other words:
let foo: Token<Literal> = // ...
foo.description // "Token with literal"
let foo2: any CustomStringConvertible = foo
foo2.description // "Regular token"
func getDescription<T: CustomStringConvertible>(_ thing: T) -> String {
return thing.description
}
getDescription(foo) // "Regular token"
You are allowed to shadow protocol requirements; as @bbrk24 says, this means that you can invoke the shadowing implementation on a value of concrete type.
However, this does not change that a type can conform to a protocol in exactly one way: where the conformance is unconditional, so too is the requirement that satisfies that conformance.
I don't know, if it's any help to you, but I would normally make Literal and Keyword conform to CustomStringConvertible or some other protocol and thus defer the decision to the generic type.
protocol LiteralOrNot
{
var isLiteral: Bool { get }
}
extension LiteralOrNot
{
var isLiteral: Bool { false }
}
struct Token<T: LiteralOrNot>
{
let lexme: T
}
struct Literal: LiteralOrNot
{
let isLiteral = true
}
struct Keyword: LiteralOrNot { }
extension Token: CustomStringConvertible
{
var description: String { lexme.isLiteral ? "Token with literal" : "Regular token" }
}
print(Token(lexme: Keyword())) /// Regular token
print(Token(lexme: Literal())) /// Token with literal
If the passed instance conforms to CustomStringConvertible , the String(describing:) initializer and the print(_:) function use the instance’s custom description property.
It would seem that print is performing type erasure and hence the non-specialised description is being used.
It's allowed for the same reason method overloads in other contexts are allowed. The potential for confusion only exists when the overloaded method also has the same name as a protocol requirement of some protocol conformed to by the concrete type.