Why is `LosslessStringConvertible.init(argument:)` non-conditional?

I found this line:

Is it a bug, or is there a reason this extension isn't conditional (Self: ExpressibleByArgument)?

PS

(Xcode 11.3.1)
There's a weird bug that prevents me from compiling the repo if I build from git-clone. It does compile as part of other projects though. It seems to work if I delete PackageManagerTests.

I'm not sure I understand your question correctly, so if my answer reads like I'm not getting what you're asking, would you mind writing out the conditional extension that you'd expect, in order to clarify?


My interpretation of the default implementation of ExpressibleByArgument's only requirement on LosslessStringConvertible is simply as follows:

Let's say we have this very similar protocol:

/// A type that can be expressed as a foo string.
public protocol ExpressibleByFoo {
    /// Creates a new instance of this type from a foo string.
    init?(foo: String)
}

And we'd like every type for which it makes sense to conform to it. So we'd have, among many others:

extension Int: ExpressibleByFoo {
    public init?(foo: String) { self.init(foo) }
}
extension Int8: ExpressibleByFoo {
    public init?(foo: String) { self.init(foo) }
}
extension Int16: ExpressibleByFoo {
    public init?(foo: String) { self.init(foo) }
}
// and so on for Int32 etc ...

But that could be written much shorter like this:

extension LosslessStringConvertible {
    public init?(foo: String) { self.init(foo) }
}

extension Int: ExpressibleByFoo {}
extension Int8: ExpressibleByFoo {}
extension Int16: ExpressibleByFoo {}
// and so on for every LosslessStringConvertible type ...

Note that there's no way, at least that I know of, to write it even shorter, not having to be explicit about the conformance for each conforming type, ie anything like these will result in compile time errors:

extension Self: ExpressibleByFoo where Self: LosslessStringConvertible {
    public init?(foo: String) { self.init(foo) }
}

or

extension LosslessStringConvertible: ExpressibleByFoo {
    public init?(foo: String) { self.init(foo) }
}

There's this explanation in SE-0143:

Stating a non-conditional conformance to a protocol implicitly states conformances to any of the protocols that the protocol inherits: one can declare conformance to the Collection protocol, and it implies conformance to Sequence as well. However, with conditional conformances, the constraints for the conformance to the inherited protocol may not be clear, and even when there is a clear choice, it will often be incorrect, so the conformance to the inherited protocol will need to be stated explicitly.


If ExpressibleByArgument had been part of the standard library, LosslessStringConvertible could have inherited from it, and there would be no need to explicitly state the conformance for each LosslessStringConvertible type individually.

Definitely an oversight, thanks for noticing that!

@Jens, the change is to add a where Self: ExpressibleByArgument constraint to the extension. Right now, every LosslessStringConvertible type gets that initializer, even those for which you haven't declared ExpressibleByArgument conformance. That's bad form. With the constraint, the rest of the conformance declarations will still work, and the init will only show up where appropriate.

Ah! :man_facepalming: Of course.

Terms of Service

Privacy Policy

Cookie Policy