How to add new string interpolations to an existing type?

SE–0228: Fix ExpressibleByStringInterpolation made it possible to have string interpolations with labels and multiple parameters. The proposal’s examples include an HTML type which accepts interpolations like:

let x: HTML = "\(url: origin, withAllowedCharacters: .urlQueryAllowed)"

However, sometimes I want to add interpolations like that to an existing type. For example, String has an initializer that takes an integer and radix:

let s = String(n, radix: 16)

I would like to be able to write \(n, radix: 16) as an interpolation within a larger string. In particular, I do not want to write \(String(n, radix: 16)).

I tried adding appendInterpolation(_:radix:) as a mutating function in an extension of String, but I get a compile-time error when I try to use it.

Is there a way that I can make this work?

String uses DefaultStringInterpolation. You need to add appendInterpolation there:

extension DefaultStringInterpolation {
    mutating func appendInterpolation(_ n: Int, radix: Int) {
        appendLiteral(String(n, radix: radix))
    }
}

The caveat is that all types using this interpolation scheme will be affected (which is more-or-less expected).

Edit:

You can also add it to any interpolation scheme with String as a literal type, but I doubt it’d be any more useful:

extension StringInterpolationProtocol where StringLiteralType == String {
  ...
}
2 Likes

Thanks!

That option is probably ill‐advised. One of the main reasons to create and use a conformer besides DefaultStringInterpolation is in order to restrict the available interpolations at compile time as a correctness aid in a particular situation. By extending every such type wholesale, you may be shooting yourself in the foot. Instead I would create a new protocol that refines it, and conform whatever interpolation engines you care about to that protocol:

protocol RadixInterpolation: StringInterpolationProtocol where StringLiteralType == String {}
extension RadixInterpolation {
  mutating func appendInterpolation(_ n: Int, radix: Int) {
    // ...
  }
}

extension DefaultStringInterpolation: RadixInterpolation {}
extension HelpfulInterpolation: RadixInterpolation {}
extension CustomInterpolation: RadixInterpolation {}
extension MyInterpolation: RadixInterpolation {}
// But no extension to `StrictInterpolation` or `SafeInterpolation`,
// because that would be counterproductive.

Edit: On the other hand, I guess if the extension isn’t public anyway, and you don’t expect to use a safety interpolator anywhere in your own code, then the extra effort probably isn’t worth it.

3 Likes