Allowing this is actually one of the explicit design goals of this proposal, and one of the main reasons for the separate StringInterpolation type. This is also why we go with statement-based append constructs rather than composing expressions: statements are more amenable to conformers tracking interval state, while expressions require everything be expressed in a homogenous return type.
Sorry, been dealing with a small-scale family emergency yesterday and today.
To save space, the proposal links to this gist with three examples of conforming types. One of them is LocalizableString
, which builds a format string that can be looked up in a Foundation localization table. This is absolutely an example I've been thinking about from the very beginning.
We've also looked at logging, and in particular os_log()
. Generating a format string at compile time obviously requires compiler support, but this proposal is great for that because it preserves a lot more information about literal vs. interpolated segments for analysis in later parts of the compiler, and we can use appendInterpolation
overload resolution to select different conversion specifier behaviors. (For example, if overload resolution selects appendInterpolation(_: Int)
, we know to use %ld
; if it selects appendInterpolation(hex: Int)
, we know to use %lx
.) This proposal doesn't have anything logging-specific in it, but in terms of enabling future logging features, I think it's a step forward.
Ahh, so it would be possible but may require creation of a format string on every use? Or is the assumption that the compiler is/grows to be smart enough to optimize out the creation of the format string as well?
Some combination of the eventual @compilerEvaluable
and custom optimization passes would statically create the format string and payload-containers, for performance-sensitive conformers (e.g. logging).
@beccadax I have been trying out the new string interpolation implementation (from the re-rebuffer branch) and it looks awesome. I have a couple of questions (or feature requests) and would like to know your thoughts on these:
(a) Are appendInterpolation
functions with default parameters supported? If not, can they be supported?
For instance, in the code snippet shown below the compiler couldn't resolve the appendInterpolation
call (I am using -swift-version 5 option).
struct MyInterpolation : ExpressibleByStringInterpolation {
struct StringInterpolation : StringInterpolationProtocol {
mutating func appendInterpolation( _ number: Int, _ asHex: Bool = false) {
...
}
}
}
"\(10)" as MyInterpolation
This feature seems to be quite useful when the conversion of a type like Int
to string can be configured by additional parameters (like display-as-hex etc). These configuration parameters can be made optional parameters.
(b) Would it be reasonable to add a finalize
method on the StringInterpolationProtocol
, which would be called when all the segments have been added?
With this extension, the sequence of calls generated for "hello \(name)!" would look like
String(stringInterpolation: {
var temp = String.StringInterpolation(...)
temp.appendLiteral("hello ")
temp.appendInterpolation(name)
temp.appendLiteral("!")
temp.finalize()
return temp
}
This could be useful in cases where the construction of the string buffer depends on the type of the interpolations and not just the number of interpolations. So some parts the string buffer can be constructed only after seeing all interpolations.
(In the existing protocol, it is possible to identify the last call to appendInterpolation
by making use of the interpolationCount
provided in the initializer. But, it seems there are scenarios where having a finalize() method could be a lot cleaner e.g. if an appendInterpolation
method calls itself recursively or calls other appendInterpolation
methods.)
The current branch is actually interpolation-rework; here’s the pull request for it.
Default arguments are (intended to be) supported. If they’re not working for you on interpolation-rework with -swift-version 5, let me know, because that would be really weird.
There isn’t a finalize()
call, but init(stringInterpolation:)
can perform any necessary finalization.
Default arguments are (intended to be) supported. If they’re not working for you on interpolation-rework with -swift-version 5, let me know, because that would be really weird.
Sounds great. I will try it out.
There isn’t a
finalize()
call, butinit(stringInterpolation:)
can perform any necessary finalization.
Yes, but finalize()
is generally a mutating function and it couldn't be directly called on the parameter passed to init(stringInterpolation:)
. It is possible to copy the parameter into a mutable variable and then call finalize()
... Are there other better ways of doing it?