Very strange automatic behavior between StringLiteralConvertible and pattern matching


(David Hart) #1

How is it that Swift allows code like this:

struct Sneaky: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}
}

func ~=(sneaky: Sneaky, string: String) -> Bool {
  return false
}

enum NormalEnum: String {
  case Super = "super"
  case Mario = "mario"
}

let value = NormalEnum(rawValue: "super”) // return nil!!!!

It hit completely by surprise today because of of a Regex library:

struct Regex: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}

  //...
}

func ~=(regex: Regex, string: String) -> Bool {
  return regex.matches(string)
}

If I was not already a Swift enthusiast, this behaviour would have left me completely dumbfounded.
What can we do about it?

David.


(David Hart) #2

Sorry about the double post.

···

On 05 Jan 2016, at 18:26, David Hart via swift-users <swift-users@swift.org> wrote:

How is it that Swift allows code like this:

struct Sneaky: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}
}

func ~=(sneaky: Sneaky, string: String) -> Bool {
  return false
}

enum NormalEnum: String {
  case Super = "super"
  case Mario = "mario"
}

let value = NormalEnum(rawValue: "super”) // return nil!!!!

It hit completely by surprise today because of of a Regex library:

struct Regex: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}

  //...
}

func ~=(regex: Regex, string: String) -> Bool {
  return regex.matches(string)
}

If I was not already a Swift enthusiast, this behaviour would have left me completely dumbfounded.
What can we do about it?

David.

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


(David Hart) #3

To bring a little bit more context: I copied this Regex library in my project which had StringLiteralConvertible and implemented the pattern matching operator and all of a sudden, ALL init(rawValue: String) calls of completely unrelated enums started returning unexpected values. If I did not have unit tests, I probably would not have found out about it for a while.

I understand the mechanism which calls StringLiteralConvertible's init(stringLiteral) under the hood:

let a: SomeStringLiteralConvertibleType = "hello"

I also understand the magic in the pattern matching operator. But I don't understand why when associating them together:

func ~=(lhs: SomeStringLiteralConvertibleType, rhs: String) -> Bool {
    return false
}

Then creates his behaviour in all Enums with String raw values:

enum MyEnum: String {
    case Super = "super"
}

let a = MyEnum(rawValue: "super") // nil

I can't figure out if this is just a confusing behaviour of Swift, in which case I want to write a proposal to make it less confusing, or if it is a big with Swift, in which case I should open a bug report.

David

···

On 05 Jan 2016, at 18:26, David Hart via swift-users <swift-users@swift.org> wrote:

How is it that Swift allows code like this:

struct Sneaky: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}
}

func ~=(sneaky: Sneaky, string: String) -> Bool {
  return false
}

enum NormalEnum: String {
  case Super = "super"
  case Mario = "mario"
}

let value = NormalEnum(rawValue: "super”) // return nil!!!!

It hit completely by surprise today because of of a Regex library:

struct Regex: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}

  //...
}

func ~=(regex: Regex, string: String) -> Bool {
  return regex.matches(string)
}

If I was not already a Swift enthusiast, this behaviour would have left me completely dumbfounded.
What can we do about it?

David.

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


(David Hart) #4

I'm starting to understand. The implementation of RawRepresentable's probably looks like this:

init?(rawValue: String) {
    switch rawValue {
    case "firstValue": self = .FirstCase
    case "secondValue": self = .SecondCase
    default: return nil
    }
}

In that case (no pun intended), the switch cases are converted to SomeStringLiteralConvertibleType and then pattern matched.

But shouldn't the implementation of switch refrain from any complicated casting when the types correspond?

David.

···

On 06 Jan 2016, at 10:20, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

To bring a little bit more context: I copied this Regex library in my project which had StringLiteralConvertible and implemented the pattern matching operator and all of a sudden, ALL init(rawValue: String) calls of completely unrelated enums started returning unexpected values. If I did not have unit tests, I probably would not have found out about it for a while.

I understand the mechanism which calls StringLiteralConvertible's init(stringLiteral) under the hood:

let a: SomeStringLiteralConvertibleType = "hello"

I also understand the magic in the pattern matching operator. But I don't understand why when associating them together:

func ~=(lhs: SomeStringLiteralConvertibleType, rhs: String) -> Bool {
    return false
}

Then creates his behaviour in all Enums with String raw values:

enum MyEnum: String {
    case Super = "super"
}

let a = MyEnum(rawValue: "super") // nil

I can't figure out if this is just a confusing behaviour of Swift, in which case I want to write a proposal to make it less confusing, or if it is a big with Swift, in which case I should open a bug report.

David

On 05 Jan 2016, at 18:26, David Hart via swift-users <swift-users@swift.org> wrote:

How is it that Swift allows code like this:

struct Sneaky: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}
}

func ~=(sneaky: Sneaky, string: String) -> Bool {
  return false
}

enum NormalEnum: String {
  case Super = "super"
  case Mario = "mario"
}

let value = NormalEnum(rawValue: "super”) // return nil!!!!

It hit completely by surprise today because of of a Regex library:

struct Regex: StringLiteralConvertible {
  init(stringLiteral value: String) {}
  init(extendedGraphemeClusterLiteral value: String) {}
  init(unicodeScalarLiteral value: String) {}

  //...
}

func ~=(regex: Regex, string: String) -> Bool {
  return regex.matches(string)
}

If I was not already a Swift enthusiast, this behaviour would have left me completely dumbfounded.
What can we do about it?

David.

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

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


(Bruno Berisso) #5

Hi,

Could you point to what Regex code are you using? You mention you copy some
library but don't provide the original source.

···

On Wed, Jan 6, 2016 at 9:12 AM David Hart via swift-evolution < swift-evolution@swift.org> wrote:

I'm starting to understand. The implementation of RawRepresentable's
probably looks like this:

init?(rawValue: String) {
    switch rawValue {
    case "firstValue": self = .FirstCase
    case "secondValue": self = .SecondCase
    default: return nil
    }
}

In that case (no pun intended), the switch cases are converted to SomeStringLiteralConvertibleType
and then pattern matched.

But shouldn't the implementation of switch refrain from any complicated
casting when the types correspond?

David.

On 06 Jan 2016, at 10:20, David Hart via swift-evolution < > swift-evolution@swift.org> wrote:

To bring a little bit more context: I copied this Regex library in my
project which had StringLiteralConvertible and implemented the pattern
matching operator and all of a sudden, ALL init(rawValue: String) calls of
completely unrelated enums started returning unexpected values. If I did
not have unit tests, I probably would not have found out about it for a
while.

I understand the mechanism which calls StringLiteralConvertible's
init(stringLiteral) under the hood:

let a: SomeStringLiteralConvertibleType = "hello"

I also understand the magic in the pattern matching operator. But I don't
understand why when associating them together:

func ~=(lhs: SomeStringLiteralConvertibleType, rhs: String) -> Bool {
    return false
}

Then creates his behaviour in all Enums with String raw values:

enum MyEnum: String {
    case Super = "super"
}

let a = MyEnum(rawValue: "super") // nil

I can't figure out if this is just a confusing behaviour of Swift, in
which case I want to write a proposal to make it less confusing, or if it
is a big with Swift, in which case I should open a bug report.

David

On 05 Jan 2016, at 18:26, David Hart via swift-users < > swift-users@swift.org> wrote:

How is it that Swift allows code like this:

struct Sneaky: StringLiteralConvertible {
init(stringLiteral value: String) {}
init(extendedGraphemeClusterLiteral value: String) {}
init(unicodeScalarLiteral value: String) {}
}

func ~=(sneaky: Sneaky, string: String) -> Bool {
return false
}

enum NormalEnum: String {
case Super = "super"
case Mario = "mario"
}

let value = NormalEnum(rawValue: "super”) // return nil!!!!

It hit completely by surprise today because of of a Regex library:

struct Regex: StringLiteralConvertible {
init(stringLiteral value: String) {}
init(extendedGraphemeClusterLiteral value: String) {}
init(unicodeScalarLiteral value: String) {}

//...
}

func ~=(regex: Regex, string: String) -> Bool {
return regex.matches(string)
}

If I was not already a Swift enthusiast, this behaviour would have left me
completely dumbfounded.
What can we do about it?

David.

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

_______________________________________________
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