[swift-evolution] ExpressibleByStringInterpolation vs. String re-evaluation vs. Regex
Jacob Bandes-Storch
jtbandes at gmail.com
Wed Aug 3 22:40:31 CDT 2016
Here's another example use case: Auto Layout visual format strings.
https://gist.github.com/jtbandes/9c1c25ee4996d2554375#file-constraintcollection-swift-L85-L87
Since only views and numeric types are supported, the generic init<T> has
to be a run-time error. Ideally it could be a compile-time error.
On Tue, Aug 2, 2016 at 6:10 PM, Brent Royal-Gordon <brent at architechies.com>
wrote:
> > On Jul 30, 2016, at 10:35 PM, Jacob Bandes-Storch via swift-evolution <
> swift-evolution at swift.org> wrote:
> >
> > In the past, there has been some interest in refining the behavior of
> ExpressibleByStringInterpolation (née StringInterpolationConvertible), for
> example:
> >
> > - Ability to restrict the types that can be used as interpolation
> segments
> > - Ability to distinguish the string-literal segments from interpolation
> segments whose type is String
> >
> > Some prior discussions:
> > - "StringInterpolationConvertible and StringLiteralConvertible
> inheritance"
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/017654.html
> > - Sub-discussion in "Allow multiple conformances to the same protocol"
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160606/020746.html
> > - "StringInterpolationConvertible: can't distinguish between literal
> components and String arguments" https://bugs.swift.org/browse/SR-1260 /
> rdar://problem/19800456&18681780
> > - "Proposal: Deprecate optionals in string interpolation"
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/018000.html
> >
> >
> > About Swift 4, Chris wrote:
> > - String re-evaluation: String is one of the most important fundamental
> types in the language. The standard library leads have numerous ideas of
> how to improve the programming model for it, without jeopardizing the goals
> of providing a unicode-correct-by-default model. Our goal is to be better
> at string processing than Perl!
> >
> > I'd be interested in any more detail the team can provide on this. I'd
> like to talk about string interpolation improvements, but it wouldn't be
> wise to do so without keeping an eye towards possible regex/pattern-binding
> syntax, and the String refinements that the stdlib team has in mind, if
> there's a chance they would affect interpolation.
> >
> > Discuss!
>
> I'm not one of the core team, so all I can really provide is a use case.
>
> Given a LocalizedString type like:
>
> /// Conforming types can be included in a LocalizedString.
> protocol LocalizedStringConvertible {
> /// The format to use for this instance. This format string will
> be included in the key when
> /// this type is interpolated into a LocalizedString.
> var localizedStringFormat: String { get }
>
> /// The arguments to use when formatting to represent this
> instance.
> var localizedStringArguments: [CVarArg] { get }
> }
>
> extension NSString: LocalizedStringConvertible {…}
> extension String: LocalizedStringConvertible {…}
> extension LocalizedString: LocalizedStringConvertible {…}
>
> extension Int: LocalizedStringConvertible {…}
> // etc.
>
> struct LocalizedString {
> /// Initializes a LocalizedString by applying the `arguments` to
> the format string with the
> /// indicated `key` using `String.init(format:arguments:)`.
> ///
> /// If the `key` does not exist in the localized string file, the
> `key` itself will be used as
> /// the format string.
> init(key: String, formattedWith arguments: [CVarArg]) {…}
> }
>
> extension String {
> init(_ localizedString: LocalizedString) {
> self.init(describing: localizedString)
> }
> }
>
> extension LocalizedString {
> /// Initializes a LocalizedString with no arguments which uses the
> indicated `key`. `%`
> /// characters in the `key` will be converted to `%%`.
> ///
> /// If the `key` does not exist in the localized string file, the
> `key` itself will be used as
> /// the string.
> init(key: String) {…}
>
> /// Initializes a LocalizedString to represent the indicated
> `value`.
> init(_ value: LocalizedStringConvertible) {…}
>
> /// Initializes a LocalizedString to represent the empty string.
> init() {…}
> }
>
> extension LocalizedString: CustomStringConvertible {…}
>
> extension LocalizedString: ExpressibleByStringLiteral {
> init(stringLiteral value: String) {
> self.init(key: value)
> }
> …
> }
>
> The current ExpressibleByStringInterpolation protocol has a number of
> defects.
>
> 1. We want to only permit LocalizedStringConvertible types, or at
> least *use* the LocalizedStringConvertible conformance; neither of these
> appears to be possible. (`is` and `as?` casts always fail, overloads don't
> seem to be called, etc.)
>
> 2. The literal parts of the string are interpreted using
> `String`'s `ExpressibleByStringLiteral` conformance; we really want them to
> use `LocalizedString`'s instead.
>
> 3. We don't want the literal parts of the string to pass through
> `init(stringInterpolationSegment:)`, because we want to treat interpolation
> and literal segments differnetly.
>
Yep, this is what I filed https://bugs.swift.org/browse/SR-1260 for.
>
> In other words, we want to be able to write something like this:
>
> extension LocalizedString: ExpressibleByStringInterpolation {
> typealias StringInterpolatableType =
> LocalizedStringConvertible
>
> init(stringInterpolation segments: LocalizedString) {
> self.init()
> for segment in segments {
> formatKey += segment.formatKey
> arguments += segment.arguments
> }
> }
>
> init(stringInterpolationSegment expr:
> LocalizedStringConvertible) {
> self.init(expr)
> }
> }
>
> And change the code generated by the compiler from (given the statement
> `"foo \(bar) baz" as LocalizedString`) this:
>
> LocalizedString(stringInterpolation:
> LocalizedString(stringInterpolationSegment:
> String(stringLiteral: "foo ")),
> LocalizedString(stringInterpolationSegment: bar),
> LocalizedString(stringInterpolationSegment:
> String(stringLiteral: " baz"))
> )
>
> To this:
>
> LocalizedString(stringInterpolation:
> LocalizedString(stringLiteral: "foo "),
> LocalizedString(stringInterpolationSegment: bar),
> LocalizedString(stringLiteral: " baz")
> )
>
> This would obviously require a few changes to the
> ExpressibleAsStringInterpolation protocol:
>
> // You cannot accept interpolations unless you can also be a plain
> literal.
> // Necessary for literal segments.
> protocol ExpressibleByStringInterpolation:
> ExpressibleByStringLiteral {
> // An associated type for the type of a permitted
> interpolation
> associatedtype StringInterpolatableType = Any
>
> // No changes here
> init(stringInterpolation segments: Self...)
>
> // No longer generic; instead uses
> StringInterpolatableType existentials.
> // Also a semantic change: this is only called for the
> actual interpolations.
> // init(stringLiteral:) is called for literal segments.
> init(stringInterpolationSegment expr:
> StringInterpolatableType)
>
> // Given the change in roles, we might want to consider
> renaming the initializers:
> //
> // init(stringInterpolation:) =>
> init(combinedStringLiteral:) or init(stringInterpolationSegments:)
> // init(stringInterpolationSegment:) =>
> init(stringInterpolation:)
> }
>
> Or perhaps we would hoist the combining initializer up into
> ExpressibleAsStringLiteral, and generate an `init(combinedStringLiteral:)`
> call every time string literals are used.
>
> protocol ExpressibleByStringLiteral {
> associatedtype StringLiteralType:
> _ExpressibleByBuiltinStringLiteral = String
>
> init(stringLiteralSegments segments: Self...)
> init(stringLiteral value: StringLiteralType)
> }
>
> protocol ExpressibleByStringInterpolation:
> ExpressibleByStringLiteral {
> associatedtype StringInterpolatableType = Any
>
> init(stringInterpolation expr: StringInterpolatableType)
> }
>
> // "foo" as LocalizedString
> LocalizedString(stringLiteralSegments:
> LocalizedString(stringLiteral: "foo")
> )
>
> // "foo \(bar) baz" as LocalizedString
> LocalizedString(stringInterpolation:
> LocalizedString(stringLiteral: "foo "),
> LocalizedString(stringInterpolation: bar),
> LocalizedString(stringLiteral: " baz")
> )
>
> Now, it's quite possible--perhaps even likely--that there are really good
> reasons for the current design. But I've been thinking about this for two
> years and I don't know what they are yet; nor can I find much relevant
> design documentation. I, too, would love to find out why the current design
> was selected.
>
> --
> Brent Royal-Gordon
> Architechies
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160803/2fe56ec8/attachment.html>
More information about the swift-evolution
mailing list