Proposal: String literal suffixes for defining types


(Lily Ballard) #1

The fact that Swift uses the exact same syntax ("a") for String, Character, and UnicodeScalar can get annoying at times. When unconstrained it's always String, and the only way to disambiguate between the two when used in a context that takes both Character and UnicodeScalar is to explicitly constraint it further, e.g. with `"a" as Character`. A trivial example of this in action is

var s = ""
s.append("c")

This code looks pretty straightforward, but it throws an ambiguity error (and the annoying part is, it doesn't actually matter which way it's resolved, the behavior is the same).

Having to type `"c" as Character` and `"c" as UnicodeScalar` can get pretty annoying if you have to do it a lot. It would be great to reduce this typing a bit.

My initial proposal here is to introduce string literal suffixes. Let me type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although that's the default if unconstrained). AFAIK this is not ambiguous with the current syntax, as I don't believe it's ever legal to put an identifier like that after a string.

Ideally this could be extended by library code with new suffixes. The obvious way to do that is to allow for a new type of "operator" declaration, perhaps something like `literal operator c { type Character }` (where `literal` is a context-sensitive keyword), which declares the suffix and the type (the actual type construction will be done with StringLiteralConvertible, StringInterpolationConvertible, UnicodeScalarLiteralConvertible, or ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared in two different modules that are imported into the same program, they're unambiguous within their source modules, and within the parent module using the suffix operator will be ambiguous unless further constrained (e.g. similar to using a function that's overloaded on its return type).

One possible library use for this would be something like `"^\s*foo"r ` for a regular expression (although regular expressions probably also want some sort of "raw" string literal syntax to deal with escapes, but that's orthogonal to this proposal).

Syntax is up for debate. I picked postfix characters because I believe it's ambiguous and it has precedent, both in C's numeric literals, and in C++11's user-defined literals (although C++11 has a required underscore before the literal, I believe this is just to allow C++11 to define its own new suffixes without ambiguity). This syntax could also work for numeric literals, come to think of it, with the same declaration and using IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work for array and dictionary literals too, though that's less likely to be useful.

FWIW, Playground color and image literals have the form [#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and [#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve the original problem (of having to type out the full type name) so this syntax is not appropriate for this feature (but the syntax is fine for what Playgrounds use it for, which is to embed literal colors/images into the source code).

-Kevin Ballard


(Chris Lattner) #2

The fact that Swift uses the exact same syntax ("a") for String, Character, and UnicodeScalar can get annoying at times. When unconstrained it's always String, and the only way to disambiguate between the two when used in a context that takes both Character and UnicodeScalar is to explicitly constraint it further, e.g. with `"a" as Character`. A trivial example of this in action is

var s = ""
s.append("c")

This code looks pretty straightforward, but it throws an ambiguity error (and the annoying part is, it doesn't actually matter which way it's resolved, the behavior is the same).

Having to type `"c" as Character` and `"c" as UnicodeScalar` can get pretty annoying if you have to do it a lot. It would be great to reduce this typing a bit.

In principle you should also be able to say “s.append(Character(“c”))” but I agree that isn’t much better.

My initial proposal here is to introduce string literal suffixes. Let me type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although that's the default if unconstrained). AFAIK this is not ambiguous with the current syntax, as I don't believe it's ever legal to put an identifier like that after a string.

I’m not sure how I feel about this proposal :-).

On the one hand, I totally agree that this can be an issue.
On the other hand: I haven’t heard this be a wide-spread complaint. It smacks of the “123ull” sorts of suffixes in C, which are pretty grotty IMO. It reduces clarity of code by introducing syntax magic.

If you’re interested in pursuing this, I’d ask you to think about a few different things:

1) How does this generalize to integer, FP and other literals we have?
2) How do we avoid baking these into the compiler? We would want to allow the suffixes to be user extensible, and have the standard ones defined by the standard library. For example, “a”c could be sugar for c(“a”) or something.
3) There are C extensions that support things like _128 as well, should we support numbers? Keep in mind that integer literals in swift support 123_456_789 separators.
4) Exactly which ones would we add to Swift out of the box? Do we go down the C path of only having them for large ones (but ignores “short”)?

-Chris

···

On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Joe Groff) #3

I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }
struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo
"y".b // same, for Bar

-Joe

···

On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

The fact that Swift uses the exact same syntax ("a") for String, Character, and UnicodeScalar can get annoying at times. When unconstrained it's always String, and the only way to disambiguate between the two when used in a context that takes both Character and UnicodeScalar is to explicitly constraint it further, e.g. with `"a" as Character`. A trivial example of this in action is

var s = ""
s.append("c")

This code looks pretty straightforward, but it throws an ambiguity error (and the annoying part is, it doesn't actually matter which way it's resolved, the behavior is the same).

Having to type `"c" as Character` and `"c" as UnicodeScalar` can get pretty annoying if you have to do it a lot. It would be great to reduce this typing a bit.

My initial proposal here is to introduce string literal suffixes. Let me type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although that's the default if unconstrained). AFAIK this is not ambiguous with the current syntax, as I don't believe it's ever legal to put an identifier like that after a string.

Ideally this could be extended by library code with new suffixes. The obvious way to do that is to allow for a new type of "operator" declaration, perhaps something like `literal operator c { type Character }` (where `literal` is a context-sensitive keyword), which declares the suffix and the type (the actual type construction will be done with StringLiteralConvertible, StringInterpolationConvertible, UnicodeScalarLiteralConvertible, or ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared in two different modules that are imported into the same program, they're unambiguous within their source modules, and within the parent module using the suffix operator will be ambiguous unless further constrained (e.g. similar to using a function that's overloaded on its return type).

One possible library use for this would be something like `"^\s*foo"r ` for a regular expression (although regular expressions probably also want some sort of "raw" string literal syntax to deal with escapes, but that's orthogonal to this proposal).

Syntax is up for debate. I picked postfix characters because I believe it's ambiguous and it has precedent, both in C's numeric literals, and in C++11's user-defined literals (although C++11 has a required underscore before the literal, I believe this is just to allow C++11 to define its own new suffixes without ambiguity). This syntax could also work for numeric literals, come to think of it, with the same declaration and using IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work for array and dictionary literals too, though that's less likely to be useful.

FWIW, Playground color and image literals have the form [#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and [#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve the original problem (of having to type out the full type name) so this syntax is not appropriate for this feature (but the syntax is fine for what Playgrounds use it for, which is to embed literal colors/images into the source code).

-Kevin Ballard
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #4

If you’re interested in pursuing this, I’d ask you to think about a few different things:

1) How does this generalize to integer, FP and other literals we have?

I believe this same proposal should work for decimal integral and decimal FP literals, as those cannot end in a letter. It cannot work for hexadecimal numeric literals as those may end in a letter, unless we come up with an alternative syntax. My first thought for an alternative syntax is to put a # after the literal, e.g. 12#i32 or 0xABC#i32, but I'm not sure if that's actually a good idea. Or we could just say this doesn't work for hexadecimal literals. The only other literals we have are true, false, and nil, and I'm not convinced we need to support literal suffixes for those (although the #i32 suggestion would actually work there too).

2) How do we avoid baking these into the compiler? We would want to allow the suffixes to be user extensible, and have the standard ones defined by the standard library. For example, “a”c could be sugar for c(“a”) or something.

We'd re-use the existing FooLiteralConvertible protocols. As suggested in my original email, we can add a way to declare these literals, which could look like

literal operator c {
    type Character
}

Then when the compiler encounters the literal suffix c, e.g. "a"c, it would treat it the same as the literal "a" constrained to the type Character. Note that "literal" here is a declaration specifier, and not a full keyword.

This syntax does not distinguish between literal type. I think this is ok, because if you say `12c` you'll get the same error you would if you typed `let c: Character = 12`, although we could enhance this instead to say something like "error: type Character does not conform to IntegerLiteralConvertible or FloatLiteralConvertible" instead.

The only real reason to actually distinguish between literal types in this declaration is if we want to be able to reuse the same suffix for different types. My inclination is to say we don't expect there to be enough literal suffixes to bother doing that, and none of the suffixes the standard library wants to define will conflict. We could also explicitly allow this, as long as the declared types do not both conform to the same FooLiteralConvertible protocol (and if they do we'd emit an ambiguity error when used with that literal type, just as we will if two different modules declare the same suffix with different types and a module that imports both uses that suffix). I don't know if it's worth the complication though, as it means we can't know what type the literal "12"x resolves to without knowing the protocol conformances of the set of possible types.

3) There are C extensions that support things like _128 as well, should we support numbers? Keep in mind that integer literals in swift support 123_456_789 separators.

I think we should require that all literal suffixes begin with a letter, and then may use letters and numbers (but not underscores). Or more generally, it should be the same rules used for identifiers except with underscore removed.

We might also establish a convention of using a trailing _ on numeric literals when using literal suffixes, because `12_i32` looks slightly nicer than `12i32`, but I'm not proposing that as a rule (and it won't be legal for string literals because those can't end in _).

4) Exactly which ones would we add to Swift out of the box? Do we go down the C path of only having them for large ones (but ignores “short”)?

I'd propose the following:

Character : c
UnicodeScalar : us
// skip String, an un-suffixed string literal will default to String already
Int : i // or int, or omitted
UInt : u // or uint
Int16 : i16
Int32 : i32
Int64 : i64
UInt16 : u16
UInt32 : u32
UInt64 : u64
Float : f
Float32 : f32
Double : f64 // or maybe d
Float80 : f80

I've included Int but not String, because it feels a little weird to omit Int when all the other numeric types are included. String feels less weird to skip. But we could skip Int because integral literals default to Int if not otherwise constrained.

I don't think we need any more, but I'll list the following for discussion anyway:

Set : s // because array literals default to Array otherwise
// if we allow non-overlapping conflicts we can also do the following:
NSDictionary : ns
NSArray : ns
NSString : ns
// Possibly NSSet : nsset as well, but I feel like that's pushing it a bit

-Kevin Ballard

···

On Thu, Dec 10, 2015, at 09:52 PM, Chris Lattner wrote:


(Marc Knaup) #5

Too bad that string literals don't conform to a specific protocol.

This would also be a nice but doesn't work:

// StaticString: "An simple string designed to represent text that is
'knowable at compile-time'."
extension StaticString {

    var x: Int {
        return 123
    }
}

print("abc".x)

···

On Fri, Dec 11, 2015 at 11:38 PM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }
struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo
"y".b // same, for Bar

-Joe

> On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
>
> The fact that Swift uses the exact same syntax ("a") for String,
Character, and UnicodeScalar can get annoying at times. When unconstrained
it's always String, and the only way to disambiguate between the two when
used in a context that takes both Character and UnicodeScalar is to
explicitly constraint it further, e.g. with `"a" as Character`. A trivial
example of this in action is
>
> var s = ""
> s.append("c")
>
> This code looks pretty straightforward, but it throws an ambiguity error
(and the annoying part is, it doesn't actually matter which way it's
resolved, the behavior is the same).
>
> Having to type `"c" as Character` and `"c" as UnicodeScalar` can get
pretty annoying if you have to do it a lot. It would be great to reduce
this typing a bit.
>
> My initial proposal here is to introduce string literal suffixes. Let me
type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar
(and maybe `"c"s` to explicitly mean a String, although that's the default
if unconstrained). AFAIK this is not ambiguous with the current syntax, as
I don't believe it's ever legal to put an identifier like that after a
string.
>
> Ideally this could be extended by library code with new suffixes. The
obvious way to do that is to allow for a new type of "operator"
declaration, perhaps something like `literal operator c { type Character }`
(where `literal` is a context-sensitive keyword), which declares the suffix
and the type (the actual type construction will be done with
StringLiteralConvertible, StringInterpolationConvertible,
UnicodeScalarLiteralConvertible, or
ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared
in two different modules that are imported into the same program, they're
unambiguous within their source modules, and within the parent module using
the suffix operator will be ambiguous unless further constrained (e.g.
similar to using a function that's overloaded on its return type).
>
> One possible library use for this would be something like `"^\s*foo"r `
for a regular expression (although regular expressions probably also want
some sort of "raw" string literal syntax to deal with escapes, but that's
orthogonal to this proposal).
>
> Syntax is up for debate. I picked postfix characters because I believe
it's ambiguous and it has precedent, both in C's numeric literals, and in
C++11's user-defined literals (although C++11 has a required underscore
before the literal, I believe this is just to allow C++11 to define its own
new suffixes without ambiguity). This syntax could also work for numeric
literals, come to think of it, with the same declaration and using
IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work
for array and dictionary literals too, though that's less likely to be
useful.
>
> FWIW, Playground color and image literals have the form
[#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and
[#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve
the original problem (of having to type out the full type name) so this
syntax is not appropriate for this feature (but the syntax is fine for what
Playgrounds use it for, which is to embed literal colors/images into the
source code).
>
> -Kevin Ballard
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michel Fortin) #6

Speaking of syntax magic, I can't say I really had a need for suffixes, but that's because I could to resort to this on occasion:

  extension UTF16Char : UnicodeScalarLiteralConvertible {
    public init(unicodeScalarLiteral value: UnicodeScalar) {
      assert(UTF16Char.isRepresentable(value))
      self.init(Int16(value.value))
    }
    public static func isRepresentable(unicodeScalar: UnicodeScalar) -> Bool {
      switch unicodeScalar.value {
      case 0x0000 ... 0xD7FF: return true
      case 0xE000 ... 0xFFFF: return true
      default: return false
      }
    }
  }

... allowing me to do things of this sort in a way that isn't overly verbose:

  func hexvalue(digit: UTF16Char) -> UInt16? {
    switch digit {
      case "a" ... "f": return digit - "a" + 0xA
      case "A" ... "F": return digit - "A" + 0xA
      case "0" ... "9": return digit - "0"
      default: return nil
    }
  }

I only wish I could keep that "UnicodeScalarLiteralConvertible" conformance extension private within the file instead of leaking it everywhere. I guess a prefix would improve things if it allowed me to keep it private.

···

Le 11 déc. 2015 à 0:52, Chris Lattner via swift-evolution <swift-evolution@swift.org> a écrit :

On the other hand: I haven’t heard this be a wide-spread complaint. It smacks of the “123ull” sorts of suffixes in C, which are pretty grotty IMO. It reduces clarity of code by introducing syntax magic.

--
Michel Fortin
michel.fortin@michelf.ca
https://michelf.ca


(Jordan Rose) #7

We had this for a while. It made the type checker do ridiculous amounts of work trying every StringLiteralConvertible type when working on a large expression, especially one where the errors were elsewhere. That's at least partly a search order issue in the type checker, but still, magically picking a type based on the members of StringLiteralConvertible types currently in scope seems like a bad idea to me.

Jordan

···

On Dec 11, 2015, at 14:38, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }
struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo
"y".b // same, for Bar

-Joe

On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

The fact that Swift uses the exact same syntax ("a") for String, Character, and UnicodeScalar can get annoying at times. When unconstrained it's always String, and the only way to disambiguate between the two when used in a context that takes both Character and UnicodeScalar is to explicitly constraint it further, e.g. with `"a" as Character`. A trivial example of this in action is

var s = ""
s.append("c")

This code looks pretty straightforward, but it throws an ambiguity error (and the annoying part is, it doesn't actually matter which way it's resolved, the behavior is the same).

Having to type `"c" as Character` and `"c" as UnicodeScalar` can get pretty annoying if you have to do it a lot. It would be great to reduce this typing a bit.

My initial proposal here is to introduce string literal suffixes. Let me type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although that's the default if unconstrained). AFAIK this is not ambiguous with the current syntax, as I don't believe it's ever legal to put an identifier like that after a string.

Ideally this could be extended by library code with new suffixes. The obvious way to do that is to allow for a new type of "operator" declaration, perhaps something like `literal operator c { type Character }` (where `literal` is a context-sensitive keyword), which declares the suffix and the type (the actual type construction will be done with StringLiteralConvertible, StringInterpolationConvertible, UnicodeScalarLiteralConvertible, or ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared in two different modules that are imported into the same program, they're unambiguous within their source modules, and within the parent module using the suffix operator will be ambiguous unless further constrained (e.g. similar to using a function that's overloaded on its return type).

One possible library use for this would be something like `"^\s*foo"r ` for a regular expression (although regular expressions probably also want some sort of "raw" string literal syntax to deal with escapes, but that's orthogonal to this proposal).

Syntax is up for debate. I picked postfix characters because I believe it's ambiguous and it has precedent, both in C's numeric literals, and in C++11's user-defined literals (although C++11 has a required underscore before the literal, I believe this is just to allow C++11 to define its own new suffixes without ambiguity). This syntax could also work for numeric literals, come to think of it, with the same declaration and using IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work for array and dictionary literals too, though that's less likely to be useful.

FWIW, Playground color and image literals have the form [#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and [#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve the original problem (of having to type out the full type name) so this syntax is not appropriate for this feature (but the syntax is fine for what Playgrounds use it for, which is to embed literal colors/images into the source code).

-Kevin Ballard
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Martin Kühl) #8

You can also write

    s.append(c("c"))

as long as you have a `typealias c = Character` in scope, and
typealiases can be block-scoped.

Of course, whether that helps depends on where and how you encounter
the ambiguity.

-Martin

···

On 11 December 2015 at 06:52, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

The fact that Swift uses the exact same syntax ("a") for String, Character, and UnicodeScalar can get annoying at times. When unconstrained it's always String, and the only way to disambiguate between the two when used in a context that takes both Character and UnicodeScalar is to explicitly constraint it further, e.g. with `"a" as Character`. A trivial example of this in action is

var s = ""
s.append("c")

This code looks pretty straightforward, but it throws an ambiguity error (and the annoying part is, it doesn't actually matter which way it's resolved, the behavior is the same).

Having to type `"c" as Character` and `"c" as UnicodeScalar` can get pretty annoying if you have to do it a lot. It would be great to reduce this typing a bit.

In principle you should also be able to say “s.append(Character(“c”))” but I agree that isn’t much better.


(ilya) #9

var s = ""
s.append("c")

This code looks pretty straightforward,

This looks to me like you're adding a string, which can be done via

s += "c"

Yes, I know adding a character can theoretically be a bit more performant,
but why would that be a consideration for static strings? Compiler should
optimize both expressions above to the same result.

UInt : u // or uint
Int16 : i16
Int32 : i32

I think

let number = 56i32

is much less readable than

let number: Int32 = 56

so probably best not to encourage it.

···

On Sat, Dec 12, 2015 at 1:48 AM, Marc Knaup via swift-evolution < swift-evolution@swift.org> wrote:

Too bad that string literals don't conform to a specific protocol.

This would also be a nice but doesn't work:

// StaticString: "An simple string designed to represent text that is
'knowable at compile-time'."
extension StaticString {

    var x: Int {
        return 123
    }
}

print("abc".x)

On Fri, Dec 11, 2015 at 11:38 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }
struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo
"y".b // same, for Bar

-Joe

> On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> The fact that Swift uses the exact same syntax ("a") for String,
Character, and UnicodeScalar can get annoying at times. When unconstrained
it's always String, and the only way to disambiguate between the two when
used in a context that takes both Character and UnicodeScalar is to
explicitly constraint it further, e.g. with `"a" as Character`. A trivial
example of this in action is
>
> var s = ""
> s.append("c")
>
> This code looks pretty straightforward, but it throws an ambiguity
error (and the annoying part is, it doesn't actually matter which way it's
resolved, the behavior is the same).
>
> Having to type `"c" as Character` and `"c" as UnicodeScalar` can get
pretty annoying if you have to do it a lot. It would be great to reduce
this typing a bit.
>
> My initial proposal here is to introduce string literal suffixes. Let
me type something like `"c"c` for a Character and `"c"us` for a
UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although
that's the default if unconstrained). AFAIK this is not ambiguous with the
current syntax, as I don't believe it's ever legal to put an identifier
like that after a string.
>
> Ideally this could be extended by library code with new suffixes. The
obvious way to do that is to allow for a new type of "operator"
declaration, perhaps something like `literal operator c { type Character }`
(where `literal` is a context-sensitive keyword), which declares the suffix
and the type (the actual type construction will be done with
StringLiteralConvertible, StringInterpolationConvertible,
UnicodeScalarLiteralConvertible, or
ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared
in two different modules that are imported into the same program, they're
unambiguous within their source modules, and within the parent module using
the suffix operator will be ambiguous unless further constrained (e.g.
similar to using a function that's overloaded on its return type).
>
> One possible library use for this would be something like `"^\s*foo"r `
for a regular expression (although regular expressions probably also want
some sort of "raw" string literal syntax to deal with escapes, but that's
orthogonal to this proposal).
>
> Syntax is up for debate. I picked postfix characters because I believe
it's ambiguous and it has precedent, both in C's numeric literals, and in
C++11's user-defined literals (although C++11 has a required underscore
before the literal, I believe this is just to allow C++11 to define its own
new suffixes without ambiguity). This syntax could also work for numeric
literals, come to think of it, with the same declaration and using
IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work
for array and dictionary literals too, though that's less likely to be
useful.
>
> FWIW, Playground color and image literals have the form
[#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and
[#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve
the original problem (of having to type out the full type name) so this
syntax is not appropriate for this feature (but the syntax is fine for what
Playgrounds use it for, which is to embed literal colors/images into the
source code).
>
> -Kevin Ballard
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #10

Perhaps there simply should be two different functions, append(char:) and
append(unicode:)

···

On Mon, Dec 14, 2015 at 18:46 Martin Kühl <swift-evolution@swift.org> wrote:

On 11 December 2015 at 06:52, Chris Lattner via swift-evolution > <swift-evolution@swift.org> wrote:
>
>> On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> The fact that Swift uses the exact same syntax ("a") for String,
Character, and UnicodeScalar can get annoying at times. When unconstrained
it's always String, and the only way to disambiguate between the two when
used in a context that takes both Character and UnicodeScalar is to
explicitly constraint it further, e.g. with `"a" as Character`. A trivial
example of this in action is
>>
>> var s = ""
>> s.append("c")
>>
>> This code looks pretty straightforward, but it throws an ambiguity
error (and the annoying part is, it doesn't actually matter which way it's
resolved, the behavior is the same).
>>
>> Having to type `"c" as Character` and `"c" as UnicodeScalar` can get
pretty annoying if you have to do it a lot. It would be great to reduce
this typing a bit.
>
> In principle you should also be able to say “s.append(Character(“c”))”
but I agree that isn’t much better.

You can also write

    s.append(c("c"))

as long as you have a `typealias c = Character` in scope, and
typealiases can be block-scoped.

Of course, whether that helps depends on where and how you encounter
the ambiguity.

-Martin
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #11

I think you're reading too much into that one example. That was a
demonstration of where the ambiguity between Character and UnicodeScalar
matters, not the sole reason to use this. Suffice to say, I've hit the
ambiguity error between Characters and UnicodeScalars often enough to
consider it worth addressing.

-Kevin

> var s = ""
> s.append("c")
>
> This code looks pretty straightforward,

This looks to me like you're adding a string, which can be done via

s += "c"

Yes, I know adding a character can theoretically be a bit more
performant, but why would that be a consideration for static strings?
Compiler should optimize both expressions above to the same result.

> UInt : u // or uint Int16 : i16 Int32 : i32

I think

let number = 56i32

is much less readable than

let number: Int32 = 56

so probably best not to encourage it.

Too bad that string literals don't conform to a specific protocol.

This would also be a nice but doesn't work: // StaticString:"An
simple string designed to represent text that is 'knowable at compile-
time'." extensionStaticString {

var x: Int { return123 } }

print("abc".x)

I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }

struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo

"y".b // same, for Bar

-Joe

···

On Sat, Dec 12, 2015, at 03:26 AM, ilya via swift-evolution wrote:

On Sat, Dec 12, 2015 at 1:48 AM, Marc Knaup via swift-evolution <swift- > evolution@swift.org> wrote:

On Fri, Dec 11, 2015 at 11:38 PM, Joe Groff via swift-evolution <swift- >> evolution@swift.org> wrote:

On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution <swift- > evolution@swift.org> wrote:

> The fact that Swift uses the exact same syntax ("a") for String,
> Character, and UnicodeScalar can get annoying at times. When
> unconstrained it's always String, and the only way to disambiguate
> between the two when used in a context that takes both Character
> and UnicodeScalar is to explicitly constraint it further, e.g.
> with `"a" as Character`. A trivial example of this in action is

var s = ""

s.append("c")

This code looks pretty straightforward, but it throws an ambiguity
error (and the annoying part is, it doesn't actually matter which way
it's resolved, the behavior is the same).

Having to type `"c" as Character` and `"c" as UnicodeScalar` can get
pretty annoying if you have to do it a lot. It would be great to
reduce this typing a bit.

My initial proposal here is to introduce string literal suffixes. Let
me type something like `"c"c` for a Character and `"c"us` for a
UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although
that's the default if unconstrained). AFAIK this is not ambiguous with
the current syntax, as I don't believe it's ever legal to put an
identifier like that after a string.

Ideally this could be extended by library code with new suffixes. The
obvious way to do that is to allow for a new type of "operator"
declaration, perhaps something like `literal operator c { type
Character }` (where `literal` is a context-sensitive keyword), which
declares the suffix and the type (the actual type construction will be
done with StringLiteralConvertible, StringInterpolationConvertible,
UnicodeScalarLiteralConvertible, or
ExtendedGraphemeClusterLiteralConvertible). If the same suffix is
declared in two different modules that are imported into the same
program, they're unambiguous within their source modules, and within
the parent module using the suffix operator will be ambiguous unless
further constrained (e.g. similar to using a function that's
overloaded on its return type).

One possible library use for this would be something like `"^\s*foo"r
` for a regular expression (although regular expressions probably also
want some sort of "raw" string literal syntax to deal with escapes,
but that's orthogonal to this proposal).

Syntax is up for debate. I picked postfix characters because I believe
it's ambiguous and it has precedent, both in C's numeric literals, and
in C++11's user-defined literals (although C++11 has a required
underscore before the literal, I believe this is just to allow C++11
to define its own new suffixes without ambiguity). This syntax could
also work for numeric literals, come to think of it, with the same
declaration and using IntegerLiteralConvertible or
FloatLiteralConvertible. Heck, it could work for array and dictionary
literals too, though that's less likely to be useful.

FWIW, Playground color and image literals have the form
[#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and
[#Image(imageLiteral: "hi.png")#]. These are interesting, but don't
solve the original problem (of having to type out the full type name)
so this syntax is not appropriate for this feature (but the syntax is
fine for what Playgrounds use it for, which is to embed literal
colors/images into the source code).

-Kevin Ballard

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Alex Gordon) #12

If Swift is ever going to have multi-line string literals, type affixes
should be a prefix and not a suffix. Otherwise the suffix would get lost at
the end of a long string.

A nice extra use for affixes is as metadata so that your editor can do
syntax highlighting. When I write r"..." in Python (for a raw string), my
editor assumes that it is a regex and gives regex-specific highlighting.
For SQL, it can only take a guess from the content.

So I can see re"[a-z0-9]+" and sql"select * from table" adding value, even
if the latter is just a pass-through.

As far as raw strings go, the general feeling in the multi-line strings
thread was to either go for single quotes or backticks. If backticks, it
could married with this proposal as follows: any prefix + backticks = raw
string. e.g.

r`raw string "quotes"` // raw string
re`<a href="[^"]+">` // raw regex literal

- Alex

···

On Sat, Dec 12, 2015 at 9:19 PM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

I think you're reading too much into that one example. That was a
demonstration of where the ambiguity between Character and UnicodeScalar
matters, not the sole reason to use this. Suffice to say, I've hit the
ambiguity error between Characters and UnicodeScalars often enough to
consider it worth addressing.

-Kevin

On Sat, Dec 12, 2015, at 03:26 AM, ilya via swift-evolution wrote:

> var s = ""
> s.append("c")
>
> This code looks pretty straightforward,

This looks to me like you're adding a string, which can be done via

s += "c"

Yes, I know adding a character can theoretically be a bit more performant,
but why would that be a consideration for static strings? Compiler should
optimize both expressions above to the same result.

> UInt : u // or uint
> Int16 : i16
> Int32 : i32

I think

let number = 56i32

is much less readable than

let number: Int32 = 56

so probably best not to encourage it.

On Sat, Dec 12, 2015 at 1:48 AM, Marc Knaup via swift-evolution < > swift-evolution@swift.org> wrote:

Too bad that string literals don't conform to a specific protocol.

This would also be a nice but doesn't work:

// StaticString:"An simple string designed to represent text that is
'knowable at compile-time'."
extensionStaticString {

    var x: Int {
        return123
    }
}

print("abc".x)

On Fri, Dec 11, 2015 at 11:38 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }
struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo
"y".b // same, for Bar

-Joe

> On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
>
> The fact that Swift uses the exact same syntax ("a") for String,
Character, and UnicodeScalar can get annoying at times. When unconstrained
it's always String, and the only way to disambiguate between the two when
used in a context that takes both Character and UnicodeScalar is to
explicitly constraint it further, e.g. with `"a" as Character`. A trivial
example of this in action is
>
> var s = ""
> s.append("c")
>
> This code looks pretty straightforward, but it throws an ambiguity error
(and the annoying part is, it doesn't actually matter which way it's
resolved, the behavior is the same).
>
> Having to type `"c" as Character` and `"c" as UnicodeScalar` can get
pretty annoying if you have to do it a lot. It would be great to reduce
this typing a bit.
>
> My initial proposal here is to introduce string literal suffixes. Let me
type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar
(and maybe `"c"s` to explicitly mean a String, although that's the default
if unconstrained). AFAIK this is not ambiguous with the current syntax, as
I don't believe it's ever legal to put an identifier like that after a
string.
>
> Ideally this could be extended by library code with new suffixes. The
obvious way to do that is to allow for a new type of "operator"
declaration, perhaps something like `literal operator c { type Character }`
(where `literal` is a context-sensitive keyword), which declares the suffix
and the type (the actual type construction will be done with
StringLiteralConvertible, StringInterpolationConvertible,
UnicodeScalarLiteralConvertible, or
ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared
in two different modules that are imported into the same program, they're
unambiguous within their source modules, and within the parent module using
the suffix operator will be ambiguous unless further constrained (e.g.
similar to using a function that's overloaded on its return type).
>
> One possible library use for this would be something like `"^\s*foo"r `
for a regular expression (although regular expressions probably also want
some sort of "raw" string literal syntax to deal with escapes, but that's
orthogonal to this proposal).
>
> Syntax is up for debate. I picked postfix characters because I believe
it's ambiguous and it has precedent, both in C's numeric literals, and in
C++11's user-defined literals (although C++11 has a required underscore
before the literal, I believe this is just to allow C++11 to define its own
new suffixes without ambiguity). This syntax could also work for numeric
literals, come to think of it, with the same declaration and using
IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work
for array and dictionary literals too, though that's less likely to be
useful.
>
> FWIW, Playground color and image literals have the form
[#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and
[#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve
the original problem (of having to type out the full type name) so this
syntax is not appropriate for this feature (but the syntax is fine for what
Playgrounds use it for, which is to embed literal colors/images into the
source code).
>
> -Kevin Ballard
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

*_______________________________________________*
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #13

If Swift is ever going to have multi-line string literals, type affixes should be a prefix and not a suffix. Otherwise the suffix would get lost at the end of a long string.

Now that you mention it, I completely agree, great point.

-Chris

···

On Dec 13, 2015, at 10:31 PM, Alex Gordon via swift-evolution <swift-evolution@swift.org> wrote:

A nice extra use for affixes is as metadata so that your editor can do syntax highlighting. When I write r"..." in Python (for a raw string), my editor assumes that it is a regex and gives regex-specific highlighting. For SQL, it can only take a guess from the content.

So I can see re"[a-z0-9]+" and sql"select * from table" adding value, even if the latter is just a pass-through.

As far as raw strings go, the general feeling in the multi-line strings thread was to either go for single quotes or backticks. If backticks, it could married with this proposal as follows: any prefix + backticks = raw string. e.g.

r`raw string "quotes"` // raw string
re`<a href="[^"]+">` // raw regex literal

- Alex

On Sat, Dec 12, 2015 at 9:19 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think you're reading too much into that one example. That was a demonstration of where the ambiguity between Character and UnicodeScalar matters, not the sole reason to use this. Suffice to say, I've hit the ambiguity error between Characters and UnicodeScalars often enough to consider it worth addressing.

-Kevin

On Sat, Dec 12, 2015, at 03:26 AM, ilya via swift-evolution wrote:

> var s = ""
> s.append("c")
>
> This code looks pretty straightforward,

This looks to me like you're adding a string, which can be done via

s += "c"

Yes, I know adding a character can theoretically be a bit more performant, but why would that be a consideration for static strings? Compiler should optimize both expressions above to the same result.

> UInt : u // or uint
> Int16 : i16
> Int32 : i32

I think

let number = 56i32

is much less readable than

let number: Int32 = 56

so probably best not to encourage it.

On Sat, Dec 12, 2015 at 1:48 AM, Marc Knaup via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Too bad that string literals don't conform to a specific protocol.

This would also be a nice but doesn't work:
// StaticString:"An simple string designed to represent text that is 'knowable at compile-time'."
extensionStaticString {

    var x: Int {
        return123
    }
}

print("abc".x)

On Fri, Dec 11, 2015 at 11:38 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I was a little disappointed something like this didn't already work:

struct Foo: StringLiteralConvertible { var f: Foo { return self } }
struct Bar: StringLiteralConvertible { var b: Bar { return self } }

"x".f // currently a failed lookup in String; could produce a Foo
"y".b // same, for Bar

-Joe

> On Dec 10, 2015, at 11:07 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> The fact that Swift uses the exact same syntax ("a") for String, Character, and UnicodeScalar can get annoying at times. When unconstrained it's always String, and the only way to disambiguate between the two when used in a context that takes both Character and UnicodeScalar is to explicitly constraint it further, e.g. with `"a" as Character`. A trivial example of this in action is
>
> var s = ""
> s.append("c")
>
> This code looks pretty straightforward, but it throws an ambiguity error (and the annoying part is, it doesn't actually matter which way it's resolved, the behavior is the same).
>
> Having to type `"c" as Character` and `"c" as UnicodeScalar` can get pretty annoying if you have to do it a lot. It would be great to reduce this typing a bit.
>
> My initial proposal here is to introduce string literal suffixes. Let me type something like `"c"c` for a Character and `"c"us` for a UnicodeScalar (and maybe `"c"s` to explicitly mean a String, although that's the default if unconstrained). AFAIK this is not ambiguous with the current syntax, as I don't believe it's ever legal to put an identifier like that after a string.
>
> Ideally this could be extended by library code with new suffixes. The obvious way to do that is to allow for a new type of "operator" declaration, perhaps something like `literal operator c { type Character }` (where `literal` is a context-sensitive keyword), which declares the suffix and the type (the actual type construction will be done with StringLiteralConvertible, StringInterpolationConvertible, UnicodeScalarLiteralConvertible, or ExtendedGraphemeClusterLiteralConvertible). If the same suffix is declared in two different modules that are imported into the same program, they're unambiguous within their source modules, and within the parent module using the suffix operator will be ambiguous unless further constrained (e.g. similar to using a function that's overloaded on its return type).
>
> One possible library use for this would be something like `"^\s*foo"r ` for a regular expression (although regular expressions probably also want some sort of "raw" string literal syntax to deal with escapes, but that's orthogonal to this proposal).
>
> Syntax is up for debate. I picked postfix characters because I believe it's ambiguous and it has precedent, both in C's numeric literals, and in C++11's user-defined literals (although C++11 has a required underscore before the literal, I believe this is just to allow C++11 to define its own new suffixes without ambiguity). This syntax could also work for numeric literals, come to think of it, with the same declaration and using IntegerLiteralConvertible or FloatLiteralConvertible. Heck, it could work for array and dictionary literals too, though that's less likely to be useful.
>
> FWIW, Playground color and image literals have the form [#Color(colorLiteralRed: 1, blue: 0, green: 0, alpha: 1)#] and [#Image(imageLiteral: "hi.png")#]. These are interesting, but don't solve the original problem (of having to type out the full type name) so this syntax is not appropriate for this feature (but the syntax is fine for what Playgrounds use it for, which is to embed literal colors/images into the source code).
>
> -Kevin Ballard
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution