Pitch: really_is and really_as operators

I’ve got one:

Siesta has a notion of “observer ownership,” whose semantics in a nutshell are “free the observer when its owners are all deallocated.” (Details here: http://bustoutsolutions.github.io/siesta/guide/memory/\)

Never mind the details. The important thing is that owners must be objects because we care about the fact that they have an allocation lifecycle.

In Swift 2, this declaration ensured that callers used only objects as owners:

  func addObserver(observer: ResourceObserver, owner: AnyObject)

In Swift 3, however, an unsuspecting called can pass a struct as an owner. Swift helpfully wraps it in a _SwiftValue, the _SwiftValue is only weakly referenced so it’s immediately deallocated, and so the observer is immediately discarded. It’s nonsensical. The compiler should be able to detect & prevent this.

What I want is:

  func addObserver(observer: ResourceObserver, owner: AnyObjectForRealNotJustAValueWrapper)

AFAIK, this is impossible in Swift 3. Or is it?

Cheers, P

···

On Aug 29, 2016, at 11:14 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when you need to know what you’ve actually got.

It would be helpful to know why you want this. What are you trying to do?

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic type
of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly
concerned about performance you would try to use as many of Swift’s static
features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than the
suffixed punctuation. Given that they’re domain-specific, will definitely
be hard to use (given that NSString(string: "Bar”) may not “really” given
you an NSString yet that’s what you asked us to check for “*really*"),
and will be obviated by the implementation of SE-0083, I can’t see a reason
why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for classes.
Now that SE-0117 has been accepted it will be possible to do this for many
classes (I would say most if it weren’t for Objective-C classes being so
common in Swift and are imported as `open`). Supporting exhaustive pattern
matching well would require some kind of syntax for matching the runtime
type exactly. I have imagined this as being “exact match” cast operators,
which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic, but
`Subclass really_is Superclass == true`. How would you use this for classes?

···

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:

Do you have an alternative in mind for exhaustive pattern matching if we
do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution < > swift-evolution@swift.org> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until later
in Swift 3 back in May and not having been heard from since then, with the
Swift 3 release looming closer and closer. However, the predictability
gains that would have been provided by this change remain desirable for
cases where one needs to know the actual dynamic type of an entity before
any bridging magic is involved. Additionally, performance-critical code may
desire the ability to check something’s type quickly without incurring the
overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and
really_as!. These operators would only return a positive result if the type
actually was what was being asked for, instead of something that might be
able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when
you need to know what you’ve actually got.

Charles

_______________________________________________
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

If you declare the API as taking AnyObject in Swift, this should work *better* in Swift 3, since it is no longer possible to pass non-object value types to an AnyObject API by implicit conversion. If the API came from Objective-C, and uses `id` there, you could perhaps hide the `Any` version that gets imported and drop an AnyObject-taking version on top of it in a Swift overlay. I agree that we ought to have a more general feature in ObjC to say "no, really, this needs to be a reference type", but I think that's orthogonal to Charles' original request.

-Joe

···

On Sep 12, 2016, at 12:24 PM, Paul Cantrell <cantrell@pobox.com> wrote:

On Aug 29, 2016, at 11:14 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when you need to know what you’ve actually got.

It would be helpful to know why you want this. What are you trying to do?

I’ve got one:

Siesta has a notion of “observer ownership,” whose semantics in a nutshell are “free the observer when its owners are all deallocated.” (Details here: http://bustoutsolutions.github.io/siesta/guide/memory/\)

Never mind the details. The important thing is that owners must be objects because we care about the fact that they have an allocation lifecycle.

In Swift 2, this declaration ensured that callers used only objects as owners:

  func addObserver(observer: ResourceObserver, owner: AnyObject)

In Swift 3, however, an unsuspecting called can pass a struct as an owner. Swift helpfully wraps it in a _SwiftValue, the _SwiftValue is only weakly referenced so it’s immediately deallocated, and so the observer is immediately discarded. It’s nonsensical. The compiler should be able to detect & prevent this.

What I want is:

  func addObserver(observer: ResourceObserver, owner: AnyObjectForRealNotJustAValueWrapper)

AFAIK, this is impossible in Swift 3. Or is it?

Ah, you’re quite right. Verified it, and it works exactly as you describe. Of course. I’ve been worrying my little head over nothing. Thanks for clearing that up!

On investigating, turns out I’d misunderstood the larger situation from a more localized Swift 3 change in an internal utility class:

    /**
      A reference that can switched between behaving as a strong or a weak ref to an object,
      and can also hold a non-object type.

      - If the value is an object, then...
        - ...if strong == true, then StrongOrWeakRef holds a strong reference to value.
        - ...if strong == false, then StrongOrWeakRef holds a weak reference to value.

      - If the value is of a value type, then...
        - ...if strong == true, then StrongOrWeakRef holds the structure.
        - ...if strong == false, then StrongOrWeakRef immediately discards the structure.
    */
    internal struct StrongOrWeakRef<T> {
        private var strongRef: T?
        private weak var weakRef: AnyObject?
        var value: T? {
            return strongRef ?? (weakRef as? T)
        }

        init(_ value: T) {
            strongRef = value
            weakRef = value as AnyObject? // ← was `as? AnyObject` in Swift 2
        }

        var strong: Bool {
            get { return strongRef != nil }
            set { strongRef = newValue ? value : nil }
        }
    }

The marked line used to be able to just leave weakRef nil when given a value type. Now, because the as AnyObject? coercion always succeeds, it performs an unnecessary heap allocation (I think?) for a _SwiftValue that’s instantly discarded. The semantics don’t change, just the performance penalty.

That’s a case for Charles’s “is it really an object?” Unless I’m off in the weeds again. Hardly the end of the world though!

Cheers, P

···

On Sep 12, 2016, at 4:01 PM, Joe Groff <jgroff@apple.com> wrote:

On Sep 12, 2016, at 12:24 PM, Paul Cantrell <cantrell@pobox.com> wrote:

In Swift 2, this declaration ensured that callers used only objects as owners:

  func addObserver(observer: ResourceObserver, owner: AnyObject)

In Swift 3, however, an unsuspecting called can pass a struct as an owner. Swift helpfully wraps it in a _SwiftValue, the _SwiftValue is only weakly referenced so it’s immediately deallocated, and so the observer is immediately discarded. It’s nonsensical. The compiler should be able to detect & prevent this.

What I want is:

  func addObserver(observer: ResourceObserver, owner: AnyObjectForRealNotJustAValueWrapper)

AFAIK, this is impossible in Swift 3. Or is it?

If you declare the API as taking AnyObject in Swift, this should work *better* in Swift 3, since it is no longer possible to pass non-object value types to an AnyObject API by implicit conversion. If the API came from Objective-C, and uses `id` there, you could perhaps hide the `Any` version that gets imported and drop an AnyObject-taking version on top of it in a Swift overlay. I agree that we ought to have a more general feature in ObjC to say "no, really, this needs to be a reference type", but I think that's orthogonal to Charles' original request.

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly concerned about performance you would try to use as many of Swift’s static features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than the suffixed punctuation. Given that they’re domain-specific, will definitely be hard to use (given that NSString(string: "Bar”) may not “really” given you an NSString yet that’s what you asked us to check for “really"), and will be obviated by the implementation of SE-0083, I can’t see a reason why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for classes. Now that SE-0117 has been accepted it will be possible to do this for many classes (I would say most if it weren’t for Objective-C classes being so common in Swift and are imported as `open`). Supporting exhaustive pattern matching well would require some kind of syntax for matching the runtime type exactly. I have imagined this as being “exact match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic, but `Subclass really_is Superclass == true`. How would you use this for classes?

Bridging is the use case motivating the pitch. I am bringing up a related use case.

The pitch does not specify `Subclass really_is Superclass == true` and I would argue that this is not the semantics we would want. My interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type or a superclass of that type."

We discussed the exhaustive pattern matching previously in this thread: [swift-evolution] [Pitch] Exhaustive pattern matching for protocols and classes where the “exact match” cast operators were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and protocols if / when we get sealed protocols) is an important one. I also think doing it right requires the ability to match exact types (i.e. not match subclasses). Maybe there is a better mechanism than a new operators but they would certainly do the job well.

···

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Do you have an alternative in mind for exhaustive pattern matching if we do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until later in Swift 3 back in May and not having been heard from since then, with the Swift 3 release looming closer and closer. However, the predictability gains that would have been provided by this change remain desirable for cases where one needs to know the actual dynamic type of an entity before any bridging magic is involved. Additionally, performance-critical code may desire the ability to check something’s type quickly without incurring the overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when you need to know what you’ve actually got.

Charles

_______________________________________________
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

Charles clarified that indeed he was pitching casting operators that match
subclasses.

If the compiler knows that a class is sealed, why do you think there's a
new keyword needed for the compiler to prove exhaustiveness?

First, you can already match exact types by writing `type(of: instance) ==
Base.self` (which doesn't evaluate to true if instance is of a subclass of
Base).

Second, if your class hierarchy is Base > A > B > C, then _even if_ there
existed no way to match exact types (which there is), you have the option
of switching over the type of an instance, providing cases that match in
the order C, B, A, Base in order to perform a different action for each.
This requires no additional knowledge at compile time beyond what you
already stipulated for your use case, namely that the entire class
hierarchy must be known at compile time.

Third, your motivating example in the previous thread already works.
Slightly renamed to match the examples above, the following compiles:

class Base { init() { } } class A1 : Base { } class A2 : Base { } func
foo(_ b: Base) -> Int { switch b { case is A1: return 1 case is A2: return
2 case is Base: return 0 } }

let a = A1() let b = A2() foo(a) // 1 foo(b) // 2

There is a warning that `case is Base` is always true. Perhaps something
could be done about that diagnostic, since that is after all what you want
in a switch statement without a default case.

I'm sure you were aware of all of these points, so I guess I'm asking, what
exactly are you pitching?

···

On Thu, Aug 25, 2016 at 08:40 Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution < >> swift-evolution@swift.org> wrote:

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic
type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly
concerned about performance you would try to use as many of Swift’s static
features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than the
suffixed punctuation. Given that they’re domain-specific, will definitely
be hard to use (given that NSString(string: "Bar”) may not “really”
given you an NSString yet that’s what you asked us to check for “*really*"),
and will be obviated by the implementation of SE-0083, I can’t see a reason
why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for
classes. Now that SE-0117 has been accepted it will be possible to do this
for many classes (I would say most if it weren’t for Objective-C classes
being so common in Swift and are imported as `open`). Supporting
exhaustive pattern matching well would require some kind of syntax for
matching the runtime type exactly. I have imagined this as being “exact
match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic, but
`Subclass really_is Superclass == true`. How would you use this for classes?

Bridging is the use case motivating the pitch. I am bringing up a related
use case.

The pitch does not specify `Subclass really_is Superclass == true` and I
would argue that this is not the semantics we would want. My
interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?, and
really_as!. These operators would only return a positive result if the type
actually was what was being asked for, instead of something that might be
able to bridge to that type *or a superclass of that type*."

We discussed the exhaustive pattern matching previously in this thread:
The swift-evolution Archives
Week-of-Mon-20160523/018799.html where the “exact match” cast operators
were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and
protocols if / when we get sealed protocols) is an important one. I also
think doing it right requires the ability to match exact types (i.e. not
match subclasses). Maybe there is a better mechanism than a new operators
but they would certainly do the job well.

Do you have an alternative in mind for exhaustive pattern matching if we
do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution < >> swift-evolution@swift.org> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until later
in Swift 3 back in May and not having been heard from since then, with the
Swift 3 release looming closer and closer. However, the predictability
gains that would have been provided by this change remain desirable for
cases where one needs to know the actual dynamic type of an entity before
any bridging magic is involved. Additionally, performance-critical code may
desire the ability to check something’s type quickly without incurring the
overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and
really_as!. These operators would only return a positive result if the type
actually was what was being asked for, instead of something that might be
able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when
you need to know what you’ve actually got.

Charles

_______________________________________________
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

Charles clarified that indeed he was pitching casting operators that match subclasses.

Ok, I missed that.

If the compiler knows that a class is sealed, why do you think there's a new keyword needed for the compiler to prove exhaustiveness?

I said I wasn’t sure if there was a different / better way to do it. Just that this *would* do it.

First, you can already match exact types by writing `type(of: instance) == Base.self` (which doesn't evaluate to true if instance is of a subclass of Base).

This might be an alternative if the compiler adds special knowledge of this syntax to prove exhaustiveness.

Second, if your class hierarchy is Base > A > B > C, then _even if_ there existed no way to match exact types (which there is), you have the option of switching over the type of an instance, providing cases that match in the order C, B, A, Base in order to perform a different action for each. This requires no additional knowledge at compile time beyond what you already stipulated for your use case, namely that the entire class hierarchy must be known at compile time.

This order requirement is fragile. If you put Base first it will always match, which probably isn’t the intent. I would like to see a solution that requires you to match each type in the hierarchy without being subject to bugs related to ordering of the cases.

Third, your motivating example in the previous thread already works. Slightly renamed to match the examples above, the following compiles:

class Base {
  init() { }
}
class A1 : Base { }
class A2 : Base { }

func foo(_ b: Base) -> Int {
  switch b {
  case is A1: return 1
  case is A2: return 2
  case is Base: return 0
  }
}

let a = A1()
let b = A2()
foo(a) // 1
foo(b) // 2

There is a warning that `case is Base` is always true. Perhaps something could be done about that diagnostic, since that is after all what you want in a switch statement without a default case.

I'm sure you were aware of all of these points, so I guess I'm asking, what exactly are you pitching?

See above. I am looking for a solution that avoids this warning precisely because it will *not* always be true. The compiler gaining special knowledge of the `type(of: instance) == Base.self` pattern could be *part* of a solution but it still doesn’t bind a name the correct type. For example, with the Base > A > B > C hierarchy when I match `type(of: instance) == B.self` I also want a variable bound with a type of `B`. This gets pretty verbose and requires the compiler to have special knowledge of pretty specific pattern:

func foo(_ b: Base) -> Int {
  switch b {
  case let base as Base where type(of: instance) == Base.self: return 1
  case let a as A where type(of: instance) == A.self: return 2
  case let b as B where type(of: instance) == B.self: return 3
  case let c as C where type(of: instance) == C.self: return 4
  }
}

If the compiler could prove exhaustiveness here I would accept that solution. But it seems like an exact match cast operator would be much more elegant.

In any case, anything that requires matching every type in a hierarchy without being subject to case ordering bugs and doesn’t require a default clause would be acceptable to me. That is the problem I would like to see solved.

···

On Aug 25, 2016, at 9:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Thu, Aug 25, 2016 at 08:40 Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly concerned about performance you would try to use as many of Swift’s static features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than the suffixed punctuation. Given that they’re domain-specific, will definitely be hard to use (given that NSString(string: "Bar”) may not “really” given you an NSString yet that’s what you asked us to check for “really"), and will be obviated by the implementation of SE-0083, I can’t see a reason why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for classes. Now that SE-0117 has been accepted it will be possible to do this for many classes (I would say most if it weren’t for Objective-C classes being so common in Swift and are imported as `open`). Supporting exhaustive pattern matching well would require some kind of syntax for matching the runtime type exactly. I have imagined this as being “exact match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic, but `Subclass really_is Superclass == true`. How would you use this for classes?

Bridging is the use case motivating the pitch. I am bringing up a related use case.

The pitch does not specify `Subclass really_is Superclass == true` and I would argue that this is not the semantics we would want. My interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type or a superclass of that type."

We discussed the exhaustive pattern matching previously in this thread: [swift-evolution] [Pitch] Exhaustive pattern matching for protocols and classes where the “exact match” cast operators were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and protocols if / when we get sealed protocols) is an important one. I also think doing it right requires the ability to match exact types (i.e. not match subclasses). Maybe there is a better mechanism than a new operators but they would certainly do the job well.

Do you have an alternative in mind for exhaustive pattern matching if we do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until later in Swift 3 back in May and not having been heard from since then, with the Swift 3 release looming closer and closer. However, the predictability gains that would have been provided by this change remain desirable for cases where one needs to know the actual dynamic type of an entity before any bridging magic is involved. Additionally, performance-critical code may desire the ability to check something’s type quickly without incurring the overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when you need to know what you’ve actually got.

Charles

_______________________________________________
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

Charles clarified that indeed he was pitching casting operators that match
subclasses.

Ok, I missed that.

If the compiler knows that a class is sealed, why do you think there's a
new keyword needed for the compiler to prove exhaustiveness?

I said I wasn’t sure if there was a different / better way to do it. Just
that this *would* do it.

First, you can already match exact types by writing `type(of: instance) ==
Base.self` (which doesn't evaluate to true if instance is of a subclass of
Base).

This might be an alternative if the compiler adds special knowledge of
this syntax to prove exhaustiveness.

I might be in favor of that. As it is, I can write this:

func ~= <T, U>(lhs: T.Type, rhs: U.Type) -> Bool {
  return lhs == rhs
}

class Base {
  init() { }
}
class A1 : Base { }
class A2 : Base { }

let a = A1()

switch type(of: a) {
case A1.self:
  print(1)
case A2.self:
  print(2)
case Base.self:
  print(0)
default:
  fatalError()
}

It'd be nice if the compiler would know about exhaustiveness (and if I
didn't have to define my own `~=`). This is, afaict, doable without any
additional syntax in the language.

Second, if your class hierarchy is Base > A > B > C, then _even if_ there

existed no way to match exact types (which there is), you have the option
of switching over the type of an instance, providing cases that match in
the order C, B, A, Base in order to perform a different action for each.
This requires no additional knowledge at compile time beyond what you
already stipulated for your use case, namely that the entire class
hierarchy must be known at compile time.

This order requirement is fragile. If you put Base first it will always
match, which probably isn’t the intent. I would like to see a solution
that requires you to match each type in the hierarchy without being subject
to bugs related to ordering of the cases.

Given that the hierarchy is known at compile-time, a solution that would
meet your criteria (not being subject to bugs related to ordering) would be
diagnostics for unreachable cases (i.e., if Base is matched before A, `case
is A` should be flagged as unreachable).

Third, your motivating example in the previous thread already works.
Slightly renamed to match the examples above, the following compiles:

class Base { init() { } } class A1 : Base { } class A2 : Base { } func
foo(_ b: Base) -> Int { switch b { case is A1: return 1 case is A2: return
2 case is Base: return 0 } }

let a = A1() let b = A2() foo(a) // 1 foo(b) // 2

There is a warning that `case is Base` is always true. Perhaps something
could be done about that diagnostic, since that is after all what you want
in a switch statement without a default case.

I'm sure you were aware of all of these points, so I guess I'm asking,
what exactly are you pitching?

See above. I am looking for a solution that avoids this warning precisely
because it will *not* always be true. The compiler gaining special
knowledge of the `type(of: instance) == Base.self` pattern could be
*part* of a solution but it still doesn’t bind a name the correct type. For
example, with the Base > A > B > C hierarchy when I match `type(of:
instance) == B.self` I also want a variable bound with a type of `B`. This
gets pretty verbose and requires the compiler to have special knowledge of
pretty specific pattern:

func foo(_ b: Base) -> Int {
  switch b {
  case let base as Base where type(of: instance) == Base.self: return 1
  case let a as A where type(of: instance) == A.self: return 2
  case let b as B where type(of: instance) == B.self: return 3
  case let c as C where type(of: instance) == C.self: return 4
  }
}

If the compiler could prove exhaustiveness here I would accept that
solution. But it seems like an exact match cast operator would be much
more elegant.

In any case, anything that requires matching every type in a hierarchy
without being subject to case ordering bugs and doesn’t require a default
clause would be acceptable to me. That is the problem I would like to see
solved.

Looking back, it seems like diagnostics for unreachable cases would meet
your criteria exactly and would be the most straightforward. I don't think
it would even require an evolution proposal. I would love to see type(of:)
work with switch statements out-of-the-box, but that seems more esoteric.
None of this requires additional syntax, IMHO.

···

On Thu, Aug 25, 2016 at 10:07 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 25, 2016, at 9:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Thu, Aug 25, 2016 at 08:40 Matthew Johnson <matthew@anandabits.com> > wrote:

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution < >> swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution < >>> swift-evolution@swift.org> wrote:

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic
type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly
concerned about performance you would try to use as many of Swift’s static
features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than
the suffixed punctuation. Given that they’re domain-specific, will
definitely be hard to use (given that NSString(string: "Bar”) may not
“really” given you an NSString yet that’s what you asked us to check for “
*really*"), and will be obviated by the implementation of SE-0083, I
can’t see a reason why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for
classes. Now that SE-0117 has been accepted it will be possible to do this
for many classes (I would say most if it weren’t for Objective-C classes
being so common in Swift and are imported as `open`). Supporting
exhaustive pattern matching well would require some kind of syntax for
matching the runtime type exactly. I have imagined this as being “exact
match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic,
but `Subclass really_is Superclass == true`. How would you use this for
classes?

Bridging is the use case motivating the pitch. I am bringing up a
related use case.

The pitch does not specify `Subclass really_is Superclass == true` and I
would argue that this is not the semantics we would want. My
interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?, and
really_as!. These operators would only return a positive result if the type
actually was what was being asked for, instead of something that might be
able to bridge to that type *or a superclass of that type*."

We discussed the exhaustive pattern matching previously in this thread:
https://lists.swift.org/pipermail/swift-evolution/We
ek-of-Mon-20160523/018799.html where the “exact match” cast operators
were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and
protocols if / when we get sealed protocols) is an important one. I also
think doing it right requires the ability to match exact types (i.e. not
match subclasses). Maybe there is a better mechanism than a new operators
but they would certainly do the job well.

Do you have an alternative in mind for exhaustive pattern matching if we
do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution < >>> swift-evolution@swift.org> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until
later in Swift 3 back in May and not having been heard from since then,
with the Swift 3 release looming closer and closer. However, the
predictability gains that would have been provided by this change remain
desirable for cases where one needs to know the actual dynamic type of an
entity before any bridging magic is involved. Additionally,
performance-critical code may desire the ability to check something’s type
quickly without incurring the overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and
really_as!. These operators would only return a positive result if the type
actually was what was being asked for, instead of something that might be
able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when
you need to know what you’ve actually got.

Charles

_______________________________________________
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

Charles clarified that indeed he was pitching casting operators that
match subclasses.

Ok, I missed that.

If the compiler knows that a class is sealed, why do you think there's a
new keyword needed for the compiler to prove exhaustiveness?

I said I wasn’t sure if there was a different / better way to do it.
Just that this *would* do it.

First, you can already match exact types by writing `type(of: instance)
== Base.self` (which doesn't evaluate to true if instance is of a subclass
of Base).

This might be an alternative if the compiler adds special knowledge of
this syntax to prove exhaustiveness.

I might be in favor of that. As it is, I can write this:

func ~= <T, U>(lhs: T.Type, rhs: U.Type) -> Bool {
  return lhs == rhs
}

class Base {
  init() { }
}
class A1 : Base { }
class A2 : Base { }

let a = A1()

switch type(of: a) {
case A1.self:
  print(1)
case A2.self:
  print(2)
case Base.self:
  print(0)
default:
  fatalError()
}

It'd be nice if the compiler would know about exhaustiveness (and if I
didn't have to define my own `~=`). This is, afaict, doable without any
additional syntax in the language.

Second, if your class hierarchy is Base > A > B > C, then _even if_ there

existed no way to match exact types (which there is), you have the option
of switching over the type of an instance, providing cases that match in
the order C, B, A, Base in order to perform a different action for each.
This requires no additional knowledge at compile time beyond what you
already stipulated for your use case, namely that the entire class
hierarchy must be known at compile time.

This order requirement is fragile. If you put Base first it will always
match, which probably isn’t the intent. I would like to see a solution
that requires you to match each type in the hierarchy without being subject
to bugs related to ordering of the cases.

Given that the hierarchy is known at compile-time, a solution that would
meet your criteria (not being subject to bugs related to ordering) would be
diagnostics for unreachable cases (i.e., if Base is matched before A, `case
is A` should be flagged as unreachable).

That isn’t an adequate solution IMO. First, it forces an ordering of the
cases. But more importantly, it will still allow accidentally matching
superclass and omitting subclass cases (so long as there are no unreachable
cases).

You cannot accidentally match superclasses by incorrect ordering, because
it will always result in unreachable cases in a language without multiple
inheritance. As to omitting subclasses, what's the harm in that? Suppose
you had a hierarchy Base > A > B > C, but Joe the Programmer doesn't know
about C. So he switches over the cases exhaustively and uses Base, A, and B
exclusively. What problems will he encounter?

I would prefer a solution that requires *all classes* in the hierarchy to
be matched exactly. The existing casts would still work for cases when you
don’t care about that and *do* want to use a superclass to match subclasses.

I don't understand the use case. This seems esoteric and goes beyond
exhaustive pattern matching.

···

On Thu, Aug 25, 2016 at 11:28 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 25, 2016, at 11:15 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Aug 25, 2016 at 10:07 AM, Matthew Johnson <matthew@anandabits.com> > wrote:

On Aug 25, 2016, at 9:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Third, your motivating example in the previous thread already works.
Slightly renamed to match the examples above, the following compiles:

class Base { init() { } } class A1 : Base { } class A2 : Base { } func
foo(_ b: Base) -> Int { switch b { case is A1: return 1 case is A2: return
2 case is Base: return 0 } }

let a = A1() let b = A2() foo(a) // 1 foo(b) // 2

There is a warning that `case is Base` is always true. Perhaps something
could be done about that diagnostic, since that is after all what you want
in a switch statement without a default case.

I'm sure you were aware of all of these points, so I guess I'm asking,
what exactly are you pitching?

See above. I am looking for a solution that avoids this warning
precisely because it will *not* always be true. The compiler gaining
special knowledge of the `type(of: instance) == Base.self` pattern could
be *part* of a solution but it still doesn’t bind a name the correct type.
For example, with the Base > A > B > C hierarchy when I match `type(of:
instance) == B.self` I also want a variable bound with a type of `B`. This
gets pretty verbose and requires the compiler to have special knowledge of
pretty specific pattern:

func foo(_ b: Base) -> Int {
  switch b {
  case let base as Base where type(of: instance) == Base.self: return 1
  case let a as A where type(of: instance) == A.self: return 2
  case let b as B where type(of: instance) == B.self: return 3
  case let c as C where type(of: instance) == C.self: return 4
  }
}

If the compiler could prove exhaustiveness here I would accept that
solution. But it seems like an exact match cast operator would be much
more elegant.

In any case, anything that requires matching every type in a hierarchy
without being subject to case ordering bugs and doesn’t require a default
clause would be acceptable to me. That is the problem I would like to see
solved.

Looking back, it seems like diagnostics for unreachable cases would meet
your criteria exactly and would be the most straightforward. I don't think
it would even require an evolution proposal. I would love to see type(of:)
work with switch statements out-of-the-box, but that seems more esoteric.
None of this requires additional syntax, IMHO.

Diagnostics would be an improvement for sure, but aren’t a complete
solution IMO for the reasons noted above. We don’t necessarily need new
syntax but I don’t have any great alternatives at the moment.

On Thu, Aug 25, 2016 at 08:40 Matthew Johnson <matthew@anandabits.com> >> wrote:

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution < >>>> swift-evolution@swift.org> wrote:

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic
type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly
concerned about performance you would try to use as many of Swift’s static
features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than
the suffixed punctuation. Given that they’re domain-specific, will
definitely be hard to use (given that NSString(string: "Bar”) may not
“really” given you an NSString yet that’s what you asked us to check for “
*really*"), and will be obviated by the implementation of SE-0083, I
can’t see a reason why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for
classes. Now that SE-0117 has been accepted it will be possible to do this
for many classes (I would say most if it weren’t for Objective-C classes
being so common in Swift and are imported as `open`). Supporting
exhaustive pattern matching well would require some kind of syntax for
matching the runtime type exactly. I have imagined this as being “exact
match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic,
but `Subclass really_is Superclass == true`. How would you use this for
classes?

Bridging is the use case motivating the pitch. I am bringing up a
related use case.

The pitch does not specify `Subclass really_is Superclass == true` and I
would argue that this is not the semantics we would want. My
interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?,
and really_as!. These operators would only return a positive result if the
type actually was what was being asked for, instead of something that might
be able to bridge to that type *or a superclass of that type*."

We discussed the exhaustive pattern matching previously in this thread:
https://lists.swift.org/pipermail/swift-evolution/We
ek-of-Mon-20160523/018799.html where the “exact match” cast operators
were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and
protocols if / when we get sealed protocols) is an important one. I also
think doing it right requires the ability to match exact types (i.e. not
match subclasses). Maybe there is a better mechanism than a new operators
but they would certainly do the job well.

Do you have an alternative in mind for exhaustive pattern matching if
we do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution < >>>> swift-evolution@swift.org> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until
later in Swift 3 back in May and not having been heard from since then,
with the Swift 3 release looming closer and closer. However, the
predictability gains that would have been provided by this change remain
desirable for cases where one needs to know the actual dynamic type of an
entity before any bridging magic is involved. Additionally,
performance-critical code may desire the ability to check something’s type
quickly without incurring the overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?,
and really_as!. These operators would only return a positive result if the
type actually was what was being asked for, instead of something that might
be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when
you need to know what you’ve actually got.

Charles

_______________________________________________
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

Charles clarified that indeed he was pitching casting operators that match subclasses.

Ok, I missed that.

If the compiler knows that a class is sealed, why do you think there's a new keyword needed for the compiler to prove exhaustiveness?

I said I wasn’t sure if there was a different / better way to do it. Just that this *would* do it.

First, you can already match exact types by writing `type(of: instance) == Base.self` (which doesn't evaluate to true if instance is of a subclass of Base).

This might be an alternative if the compiler adds special knowledge of this syntax to prove exhaustiveness.

I might be in favor of that. As it is, I can write this:

func ~= <T, U>(lhs: T.Type, rhs: U.Type) -> Bool {
  return lhs == rhs
}

class Base {
  init() { }
}
class A1 : Base { }
class A2 : Base { }

let a = A1()

switch type(of: a) {
case A1.self:
  print(1)
case A2.self:
  print(2)
case Base.self:
  print(0)
default:
  fatalError()
}

It'd be nice if the compiler would know about exhaustiveness (and if I didn't have to define my own `~=`). This is, afaict, doable without any additional syntax in the language.

Second, if your class hierarchy is Base > A > B > C, then _even if_ there existed no way to match exact types (which there is), you have the option of switching over the type of an instance, providing cases that match in the order C, B, A, Base in order to perform a different action for each. This requires no additional knowledge at compile time beyond what you already stipulated for your use case, namely that the entire class hierarchy must be known at compile time.

This order requirement is fragile. If you put Base first it will always match, which probably isn’t the intent. I would like to see a solution that requires you to match each type in the hierarchy without being subject to bugs related to ordering of the cases.

Given that the hierarchy is known at compile-time, a solution that would meet your criteria (not being subject to bugs related to ordering) would be diagnostics for unreachable cases (i.e., if Base is matched before A, `case is A` should be flagged as unreachable).

That isn’t an adequate solution IMO. First, it forces an ordering of the cases. But more importantly, it will still allow accidentally matching superclass and omitting subclass cases (so long as there are no unreachable cases).

You cannot accidentally match superclasses by incorrect ordering, because it will always result in unreachable cases in a language without multiple inheritance. As to omitting subclasses, what's the harm in that? Suppose you had a hierarchy Base > A > B > C, but Joe the Programmer doesn't know about C. So he switches over the cases exhaustively and uses Base, A, and B exclusively. What problems will he encounter?

Maybe in that case he won’t encounter any problems. However, if you have:

Base
  > >
C1 C2
> >
D1 D2

And you just switch over Base, C1, and C2 you may have a match for all possible runtime values but you don’t have cases for all of the possible type. Imagine if you have code that is initially written before D1 and D2 are introduced and your intent is to include a case for each possible runtime type. You want the code to fail to compile when D1 and D2 are introduced until you revisit the switch statement, just as it does when you add a new case to an enum. However, given the current tools there is nothing you can do to force this. This is what I meant by “accidentally”.

Having a tool for preventing these kinds of mistakes seems like a useful thing and puts classes (and protocols if it works with them as well when all conforming types are known) on equal footing with enums in terms of pattern matching. This gives us more options for structuring our code.

···

On Aug 25, 2016, at 11:56 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Aug 25, 2016 at 11:28 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Aug 25, 2016, at 11:15 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Thu, Aug 25, 2016 at 10:07 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Aug 25, 2016, at 9:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

I would prefer a solution that requires *all classes* in the hierarchy to be matched exactly. The existing casts would still work for cases when you don’t care about that and *do* want to use a superclass to match subclasses.

I don't understand the use case. This seems esoteric and goes beyond exhaustive pattern matching.

Third, your motivating example in the previous thread already works. Slightly renamed to match the examples above, the following compiles:

class Base {
  init() { }
}
class A1 : Base { }
class A2 : Base { }

func foo(_ b: Base) -> Int {
  switch b {
  case is A1: return 1
  case is A2: return 2
  case is Base: return 0
  }
}

let a = A1()
let b = A2()
foo(a) // 1
foo(b) // 2

There is a warning that `case is Base` is always true. Perhaps something could be done about that diagnostic, since that is after all what you want in a switch statement without a default case.

I'm sure you were aware of all of these points, so I guess I'm asking, what exactly are you pitching?

See above. I am looking for a solution that avoids this warning precisely because it will *not* always be true. The compiler gaining special knowledge of the `type(of: instance) == Base.self` pattern could be *part* of a solution but it still doesn’t bind a name the correct type. For example, with the Base > A > B > C hierarchy when I match `type(of: instance) == B.self` I also want a variable bound with a type of `B`. This gets pretty verbose and requires the compiler to have special knowledge of pretty specific pattern:

func foo(_ b: Base) -> Int {
  switch b {
  case let base as Base where type(of: instance) == Base.self: return 1
  case let a as A where type(of: instance) == A.self: return 2
  case let b as B where type(of: instance) == B.self: return 3
  case let c as C where type(of: instance) == C.self: return 4
  }
}

If the compiler could prove exhaustiveness here I would accept that solution. But it seems like an exact match cast operator would be much more elegant.

In any case, anything that requires matching every type in a hierarchy without being subject to case ordering bugs and doesn’t require a default clause would be acceptable to me. That is the problem I would like to see solved.

Looking back, it seems like diagnostics for unreachable cases would meet your criteria exactly and would be the most straightforward. I don't think it would even require an evolution proposal. I would love to see type(of:) work with switch statements out-of-the-box, but that seems more esoteric. None of this requires additional syntax, IMHO.

Diagnostics would be an improvement for sure, but aren’t a complete solution IMO for the reasons noted above. We don’t necessarily need new syntax but I don’t have any great alternatives at the moment.

On Thu, Aug 25, 2016 at 08:40 Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly concerned about performance you would try to use as many of Swift’s static features as possible.

- Especially no more keywords that look like they belong in Rust or PHP!

There is no precedent for the spelling of these operations other than the suffixed punctuation. Given that they’re domain-specific, will definitely be hard to use (given that NSString(string: "Bar”) may not “really” given you an NSString yet that’s what you asked us to check for “really"), and will be obviated by the implementation of SE-0083, I can’t see a reason why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for classes. Now that SE-0117 has been accepted it will be possible to do this for many classes (I would say most if it weren’t for Objective-C classes being so common in Swift and are imported as `open`). Supporting exhaustive pattern matching well would require some kind of syntax for matching the runtime type exactly. I have imagined this as being “exact match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic, but `Subclass really_is Superclass == true`. How would you use this for classes?

Bridging is the use case motivating the pitch. I am bringing up a related use case.

The pitch does not specify `Subclass really_is Superclass == true` and I would argue that this is not the semantics we would want. My interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type or a superclass of that type."

We discussed the exhaustive pattern matching previously in this thread: [swift-evolution] [Pitch] Exhaustive pattern matching for protocols and classes where the “exact match” cast operators were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and protocols if / when we get sealed protocols) is an important one. I also think doing it right requires the ability to match exact types (i.e. not match subclasses). Maybe there is a better mechanism than a new operators but they would certainly do the job well.

Do you have an alternative in mind for exhaustive pattern matching if we do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until later in Swift 3 back in May and not having been heard from since then, with the Swift 3 release looming closer and closer. However, the predictability gains that would have been provided by this change remain desirable for cases where one needs to know the actual dynamic type of an entity before any bridging magic is involved. Additionally, performance-critical code may desire the ability to check something’s type quickly without incurring the overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?, and really_as!. These operators would only return a positive result if the type actually was what was being asked for, instead of something that might be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast when you need to know what you’ve actually got.

Charles

_______________________________________________
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

Is that what you guys wanted to achieve?

@inline(never)
func opaqueCast<T,U>(_ x: T, _: U.Type) -> Bool {
    return x is U
}

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
opaqueCast(type(of: foo), String.Type.self) // true

let fooIsNSString = foo is NSString // true
opaqueCast(type(of: foo), NSString.Type.self) // false

let barIsString = bar is String // true
opaqueCast(type(of: bar), String.Type.self) // false

let barIsNSString = bar is NSString // true
opaqueCast(type(of: bar), NSString.Type.self) // true
Joe Groff showed me this little trick when is cast was bugged in some Xcode beta for Swift 1.2.

Charles clarified that indeed he was pitching casting operators that
match subclasses.

Ok, I missed that.

If the compiler knows that a class is sealed, why do you think there's a
new keyword needed for the compiler to prove exhaustiveness?

I said I wasn’t sure if there was a different / better way to do it.
Just that this *would* do it.

First, you can already match exact types by writing `type(of: instance)
== Base.self` (which doesn't evaluate to true if instance is of a subclass
of Base).

This might be an alternative if the compiler adds special knowledge of
this syntax to prove exhaustiveness.

I might be in favor of that. As it is, I can write this:

func ~= <T, U>(lhs: T.Type, rhs: U.Type) -> Bool {
  return lhs == rhs
}

class Base {
  init() { }
}
class A1 : Base { }
class A2 : Base { }

let a = A1()

switch type(of: a) {
case A1.self:
  print(1)
case A2.self:
  print(2)
case Base.self:
  print(0)
default:
  fatalError()
}

It'd be nice if the compiler would know about exhaustiveness (and if I
didn't have to define my own `~=`). This is, afaict, doable without any
additional syntax in the language.

Second, if your class hierarchy is Base > A > B > C, then _even if_ there

existed no way to match exact types (which there is), you have the option
of switching over the type of an instance, providing cases that match in
the order C, B, A, Base in order to perform a different action for each.
This requires no additional knowledge at compile time beyond what you
already stipulated for your use case, namely that the entire class
hierarchy must be known at compile time.

This order requirement is fragile. If you put Base first it will always
match, which probably isn’t the intent. I would like to see a solution
that requires you to match each type in the hierarchy without being subject
to bugs related to ordering of the cases.

Given that the hierarchy is known at compile-time, a solution that would
meet your criteria (not being subject to bugs related to ordering) would be
diagnostics for unreachable cases (i.e., if Base is matched before A, `case
is A` should be flagged as unreachable).

That isn’t an adequate solution IMO. First, it forces an ordering of the
cases. But more importantly, it will still allow accidentally matching
superclass and omitting subclass cases (so long as there are no unreachable
cases).

You cannot accidentally match superclasses by incorrect ordering, because
it will always result in unreachable cases in a language without multiple
inheritance. As to omitting subclasses, what's the harm in that? Suppose
you had a hierarchy Base > A > B > C, but Joe the Programmer doesn't know
about C. So he switches over the cases exhaustively and uses Base, A, and B
exclusively. What problems will he encounter?

Maybe in that case he won’t encounter any problems. However, if you have:

Base
  > >
C1 C2
> >
D1 D2

And you just switch over Base, C1, and C2 you may have a match for all
possible runtime values but you don’t have cases for all of the possible
type. Imagine if you have code that is initially written before D1 and D2
are introduced and your intent is to include a case for each possible
runtime type. You want the code to fail to compile when D1 and D2 are
introduced until you revisit the switch statement

I understand that this is what you want, but what is your use case for
this? Why should you want your code to fail to compile?

To be sure, it is currently *possible* to get this behavior (at runtime,
admittedly) by having a default case that calls fatalError() after a series
of cases that explicitly compare dynamic type using type(of:) and ==, but
this is a code smell at the very least, and certainly not something we
should want to make easier with a keyword (even if that allows the failure
to manifest at compile time). For one, it breaks our understanding of
library versioning:

Library Foo defines the following class hierarchy, which is entirely public:

  Base (sealed) > A > B > C

Sally uses library Foo and uses your proposed keyword in an exhaustive
switch.

The next minor release of library Foo (and it's a minor release because the
public API is unchanged) adds an internal subclass of C called __C (this
would be analogous to how NSString and __NSCFString are related, but of
course those are Objective-C classes in Darwin Foundation).

Various methods that formerly returned a value of type C now return a value
of type __C upcast as C. Sally's code ceases to compile, and through no
fault of the library developer. There is nothing she can do using your
proposed keyword to fix this defect, because __C is not public. She *must*
use casting operators that match subclasses.

In the next minor version of library Foo, __C is no longer necessary and is
removed. Sally's code doesn't break this time, because she stopped using
your proposed keyword.

, just as it does when you add a new case to an enum. However, given the
current tools there is nothing you can do to force this. This is what I
meant by “accidentally”.

Having a tool for preventing these kinds of mistakes seems like a useful
thing and puts classes (and protocols if it works with them as well when
all conforming types are known) on equal footing with enums in terms of
pattern matching. This gives us more options for structuring our code.

I'd have to be convinced that this tool is at all wise with a compelling
use case. IMO, it encourages far, far more fragile code than a poorly
ordered series of cases would do currently. Therefore, at the moment, it
seems to me that this tool would be the greater mistake.

To be clear, your pitch has nothing to do with exhaustiveness on par with
enums, because it is possible to write switch statements over class
hierarchies either exhaustively or non-exhaustively using the existing
casting operators; the compiler can be given enough smarts to emit the
appropriate errors when the cases are non-exhaustive and to stop insisting
on a `default` case when the cases are exhaustive. I'd be absolutely in
favor of that change.

···

On Thu, Aug 25, 2016 at 3:24 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Aug 25, 2016, at 11:56 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Aug 25, 2016 at 11:28 AM, Matthew Johnson <matthew@anandabits.com> > wrote:

On Aug 25, 2016, at 11:15 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Aug 25, 2016 at 10:07 AM, Matthew Johnson <matthew@anandabits.com >> > wrote:

On Aug 25, 2016, at 9:37 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I would prefer a solution that requires *all classes* in the hierarchy to

be matched exactly. The existing casts would still work for cases when you
don’t care about that and *do* want to use a superclass to match subclasses.

I don't understand the use case. This seems esoteric and goes beyond
exhaustive pattern matching.

Third, your motivating example in the previous thread already works.
Slightly renamed to match the examples above, the following compiles:

class Base { init() { } } class A1 : Base { } class A2 : Base { } func
foo(_ b: Base) -> Int { switch b { case is A1: return 1 case is A2: return
2 case is Base: return 0 } }

let a = A1() let b = A2() foo(a) // 1 foo(b) // 2

There is a warning that `case is Base` is always true. Perhaps something
could be done about that diagnostic, since that is after all what you want
in a switch statement without a default case.

I'm sure you were aware of all of these points, so I guess I'm asking,
what exactly are you pitching?

See above. I am looking for a solution that avoids this warning
precisely because it will *not* always be true. The compiler gaining
special knowledge of the `type(of: instance) == Base.self` pattern
could be *part* of a solution but it still doesn’t bind a name the correct
type. For example, with the Base > A > B > C hierarchy when I match
`type(of: instance) == B.self` I also want a variable bound with a type of
`B`. This gets pretty verbose and requires the compiler to have special
knowledge of pretty specific pattern:

func foo(_ b: Base) -> Int {
  switch b {
  case let base as Base where type(of: instance) == Base.self: return 1
  case let a as A where type(of: instance) == A.self: return 2
  case let b as B where type(of: instance) == B.self: return 3
  case let c as C where type(of: instance) == C.self: return 4
  }
}

If the compiler could prove exhaustiveness here I would accept that
solution. But it seems like an exact match cast operator would be much
more elegant.

In any case, anything that requires matching every type in a hierarchy
without being subject to case ordering bugs and doesn’t require a default
clause would be acceptable to me. That is the problem I would like to see
solved.

Looking back, it seems like diagnostics for unreachable cases would meet
your criteria exactly and would be the most straightforward. I don't think
it would even require an evolution proposal. I would love to see type(of:)
work with switch statements out-of-the-box, but that seems more esoteric.
None of this requires additional syntax, IMHO.

Diagnostics would be an improvement for sure, but aren’t a complete
solution IMO for the reasons noted above. We don’t necessarily need new
syntax but I don’t have any great alternatives at the moment.

On Thu, Aug 25, 2016 at 08:40 Matthew Johnson <matthew@anandabits.com> >>> wrote:

On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution < >>>> swift-evolution@swift.org> wrote:

On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

I have 3 qualms with this proposal as it stands:

- type(of:) will never lie to you.

The only question it won’t answer to your satisfaction is the dynamic
type of the NSString you boxed up as an Any.

- No more keywords without significant justification.

I don’t buy the performance use case at all - if you were properly
concerned about performance you would try to use as many of Swift’s static
features as possible.

- Especially no more keywords that look like they belong in Rust or
PHP!

There is no precedent for the spelling of these operations other than
the suffixed punctuation. Given that they’re domain-specific, will
definitely be hard to use (given that NSString(string: "Bar”) may not
“really” given you an NSString yet that’s what you asked us to check for “
*really*"), and will be obviated by the implementation of SE-0083, I
can’t see a reason why we need any of this in the language proper.

One related topic to consider is exhaustive pattern matching for
classes. Now that SE-0117 has been accepted it will be possible to do this
for many classes (I would say most if it weren’t for Objective-C classes
being so common in Swift and are imported as `open`). Supporting
exhaustive pattern matching well would require some kind of syntax for
matching the runtime type exactly. I have imagined this as being “exact
match” cast operators, which is what the `really_*` operators are.

I don't understand. As pitched, these operators remove bridging magic,
but `Subclass really_is Superclass == true`. How would you use this for
classes?

Bridging is the use case motivating the pitch. I am bringing up a
related use case.

The pitch does not specify `Subclass really_is Superclass == true` and
I would argue that this is not the semantics we would want. My
interpretation of the proposed solution is:

"I propose the following operators: really_is, really_as, really_as?,
and really_as!. These operators would only return a positive result if the
type actually was what was being asked for, instead of something that might
be able to bridge to that type *or a superclass of that type*."

We discussed the exhaustive pattern matching previously in this thread:
https://lists.swift.org/pipermail/swift-evolution/We
ek-of-Mon-20160523/018799.html where the “exact match” cast operators
were called `isExactly` and `asExactly`.

I think the exhaustive pattern matching use case for classes (and
protocols if / when we get sealed protocols) is an important one. I also
think doing it right requires the ability to match exact types (i.e. not
match subclasses). Maybe there is a better mechanism than a new operators
but they would certainly do the job well.

Do you have an alternative in mind for exhaustive pattern matching if
we do not introduce exact match cast operators?

~Robert Widmann

On Aug 24, 2016, at 5:08 PM, Charles Srstka via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

MOTIVATION:

SE-0083 appears to be dead in the water, having been deferred until
later in Swift 3 back in May and not having been heard from since then,
with the Swift 3 release looming closer and closer. However, the
predictability gains that would have been provided by this change remain
desirable for cases where one needs to know the actual dynamic type of an
entity before any bridging magic is involved. Additionally,
performance-critical code may desire the ability to check something’s type
quickly without incurring the overhead of Objective-C bridging code.

PROPOSED SOLUTION:

I propose the following operators: really_is, really_as, really_as?,
and really_as!. These operators would only return a positive result if the
type actually was what was being asked for, instead of something that might
be able to bridge to that type.

DETAILED DESIGN:

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String // true
let fooReallyIsString = foo really_is String // true

let fooIsNSString = foo is NSString // true
let fooReallyIsNSString = foo really_is NSString // false

let barIsString = bar is String // true
let barReallyIsString = bar really_is String // false

let barIsNSString = bar is NSString // true
let barReallyIsNSString = bar really_is NSString // true

ALTERNATIVES CONSIDERED:

Stick with using an unholy combination of Mirror and unsafeBitCast
when you need to know what you’ve actually got.

Charles

_______________________________________________
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

let fooIsNSString = foo is NSString // true

I don't understand. Why is so? On which version of Swift did you try this?

IBM Swift Sandbox
Swift Ver. 3.0 (Aug 23, 2016)
Platform: Linux (x86_64)

import Foundation

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String
print(fooIsString) // true

let fooIsNSString = foo is NSString
print(fooIsNSString) // false! as expected

···

On 25.08.2016 22:40, Adrian Zubarev via swift-evolution wrote:

Is that what you guys wanted to achieve?

>@inline(never) func opaqueCast<T,U>(_ x: T, _: U.Type) -> Bool { return x
is U } let foo: Any = "Foo" let bar: Any = NSString(string: "Bar") let
fooIsString = foo is String // true opaqueCast(type(of: foo),
String.Type.self) // true let fooIsNSString = foo is NSString // true
opaqueCast(type(of: foo), NSString.Type.self) // false let barIsString =
bar is String // true opaqueCast(type(of: bar), String.Type.self) // false
let barIsNSString = bar is NSString // true opaqueCast(type(of: bar),
NSString.Type.self) // true |

Joe Groff showed me this little trick when |is| cast was bugged in some
Xcode beta for Swift 1.2.

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

The behavior is due to the Objective-C bridge, which I don’t think exists on the Linux version. On the Mac version, fooIsNSString will be true because of the bridging magic that lets you convert between String and NSString (and other equivalent Swift and Objective-C data types) using “as”. Never mind that “foo as NSString” isn’t even fewer characters than “NSString(foo)”; somebody decided it was easier that way. And of course, “is”, “as?” and the rest of the family have to behave the same way for consistency. As a result, you can never be 100% sure what’s actually going to happen when you use “as?”, which is one of the reasons I dislike “as?”. SE-0083 attempted to remove this bridging magic from the dynamic casts and make them only truthfully report the type of the object or struct you were looking at, but it was deferred until “later in Swift 3”, and Swift 3 has progressed past the point where source-breaking changes are accepted, so that proposal is probably dead at this point.

The source incompatibility that this bridging behavior can create between Swift on the Mac and on other platforms is actually a pretty good point in favor of SE-0083 that I wish we’d thought of before everything became locked down.

Charles

···

On Aug 25, 2016, at 4:15 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

> let fooIsNSString = foo is NSString // true

I don't understand. Why is so? On which version of Swift did you try this?

IBM Swift Sandbox
Swift Ver. 3.0 (Aug 23, 2016)
Platform: Linux (x86_64)

import Foundation

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String
print(fooIsString) // true

let fooIsNSString = foo is NSString
print(fooIsNSString) // false! as expected

Tested with Swift 3.0 Xcode 8 beta 6 playground.

···

--
Adrian Zubarev
Sent with Airmail

Am 25. August 2016 um 23:15:24, Vladimir.S (svabox@gmail.com) schrieb:

let fooIsNSString = foo is NSString // true

I don't understand. Why is so? On which version of Swift did you try this?

IBM Swift Sandbox
Swift Ver. 3.0 (Aug 23, 2016)
Platform: Linux (x86_64)

import Foundation

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String
print(fooIsString) // true

let fooIsNSString = foo is NSString
print(fooIsNSString) // false! as expected

On 25.08.2016 22:40, Adrian Zubarev via swift-evolution wrote:

Is that what you guys wanted to achieve?

>@inline(never) func opaqueCast<T,U>(_ x: T, _: U.Type) -> Bool { return x
is U } let foo: Any = "Foo" let bar: Any = NSString(string: "Bar") let
fooIsString = foo is String // true opaqueCast(type(of: foo),
String.Type.self) // true let fooIsNSString = foo is NSString // true
opaqueCast(type(of: foo), NSString.Type.self) // false let barIsString =
bar is String // true opaqueCast(type(of: bar), String.Type.self) // false
let barIsNSString = bar is NSString // true opaqueCast(type(of: bar),
NSString.Type.self) // true |

Joe Groff showed me this little trick when |is| cast was bugged in some
Xcode beta for Swift 1.2.

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

I believe that due to SE-0072 my code(Swift Ver. 3.0 (Aug 23, 2016) on Linux) is working right, no?

···

On 26.08.2016 0:18, Adrian Zubarev via swift-evolution wrote:

Tested with Swift 3.0 Xcode 8 beta 6 playground.

--
Adrian Zubarev
Sent with Airmail

Am 25. August 2016 um 23:15:24, Vladimir.S (svabox@gmail.com
<mailto:svabox@gmail.com>) schrieb:

> let fooIsNSString = foo is NSString // true

I don't understand. Why is so? On which version of Swift did you try this?

IBM Swift Sandbox
Swift Ver. 3.0 (Aug 23, 2016)
Platform: Linux (x86_64)

import Foundation

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String
print(fooIsString) // true

let fooIsNSString = foo is NSString
print(fooIsNSString) // false! as expected

On 25.08.2016 22:40, Adrian Zubarev via swift-evolution wrote:
> Is that what you guys wanted to achieve?
>
> >@inline(never) func opaqueCast<T,U>(_ x: T, _: U.Type) -> Bool { return x
> is U } let foo: Any = "Foo" let bar: Any = NSString(string: "Bar") let
> fooIsString = foo is String // true opaqueCast(type(of: foo),
> String.Type.self) // true let fooIsNSString = foo is NSString // true
> opaqueCast(type(of: foo), NSString.Type.self) // false let barIsString =
> bar is String // true opaqueCast(type(of: bar), String.Type.self) // false
> let barIsNSString = bar is NSString // true opaqueCast(type(of: bar),
> NSString.Type.self) // true |
>
> Joe Groff showed me this little trick when |is| cast was bugged in some
> Xcode beta for Swift 1.2.
>
> _______________________________________________
> 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

The behavior is due to the Objective-C bridge, which I don’t think exists

> on the Linux version. On the Mac version, fooIsNSString will be true
> because of the bridging magic that lets you convert between String and
> NSString (and other equivalent Swift and Objective-C data types) using
> “as”.

Well, if I understand correctly, exactly here we are trying to cast instance *typed* as `Any`, not as `String`. But it seems that this magic exists in Linux version too, but with little different behavior.
Just made some tests, got interested results:

Swift Ver. 3.0 (Aug 24, 2016)
Platform: Linux (x86_64)

import Foundation

let fooAsNSString = "foo" as NSString

print(type(of: fooAsNSString)) // NSString

let foo: Any = "Foo"
let fooAsString = "Foo"
let bar: Any = NSString(string: "Bar")

print(type(of: foo)) // String
print(type(of: bar)) // NSString

print(fooAsString is NSString) // false
print("foo" is NSString) // true

print(foo is NSString) // false
print(bar is NSString) // true

WARNINGS:
cast from 'String' to unrelated type 'NSString' always fails
print(fooAsString is NSString)

'is' test is always true
print("foo" is NSString)

Still, I believe that due to *SE-0072*
1) Linux version work more correctly and Mac's version should be fixed
2) There is a bug in Linux version, which produces
"foo" is NSString == true

Am I missing something?

(Just got confused.. How should I cast NSString to String in Linux(or any) version of Swift? Tried various variants like nsstring as! String and String(describing: nsstring) - got compilation/runtime error)

···

On 26.08.2016 0:31, Charles Srstka wrote:

On Aug 25, 2016, at 4:15 PM, Vladimir.S via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> let fooIsNSString = foo is NSString // true

I don't understand. Why is so? On which version of Swift did you try this?

IBM Swift Sandbox
Swift Ver. 3.0 (Aug 23, 2016)
Platform: Linux (x86_64)

import Foundation

let foo: Any = "Foo"
let bar: Any = NSString(string: "Bar")

let fooIsString = foo is String
print(fooIsString) // true

let fooIsNSString = foo is NSString
print(fooIsNSString) // false! as expected

The behavior is due to the Objective-C bridge, which I don’t think exists
on the Linux version. On the Mac version, fooIsNSString will be true
because of the bridging magic that lets you convert between String and
NSString (and other equivalent Swift and Objective-C data types) using
“as”. Never mind that “foo as NSString” isn’t even fewer characters than
“NSString(foo)”; somebody decided it was easier that way. And of course,
“is”, “as?” and the rest of the family have to behave the same way for
consistency. As a result, you can never be 100% sure what’s actually going
to happen when you use “as?”, which is one of the reasons I dislike “as?”.
SE-0083 attempted to remove this bridging magic from the dynamic casts and
make them only truthfully report the type of the object or struct you were
looking at, but it was deferred until “later in Swift 3”, and Swift 3 has
progressed past the point where source-breaking changes are accepted, so
that proposal is probably dead at this point.

The source incompatibility that this bridging behavior can create between
Swift on the Mac and on other platforms is actually a pretty good point in
favor of SE-0083 that I wish we’d thought of before everything became
locked down.

Charles

> The behavior is due to the Objective-C bridge, which I don’t think
exists
> on the Linux version. On the Mac version, fooIsNSString will be true
> because of the bridging magic that lets you convert between String and
> NSString (and other equivalent Swift and Objective-C data types) using
> “as”.

Well, if I understand correctly, exactly here we are trying to cast
instance *typed* as `Any`, not as `String`. But it seems that this magic
exists in Linux version too, but with little different behavior.
Just made some tests, got interested results:

Swift Ver. 3.0 (Aug 24, 2016)
Platform: Linux (x86_64)

import Foundation

let fooAsNSString = "foo" as NSString

print(type(of: fooAsNSString)) // NSString

let foo: Any = "Foo"
let fooAsString = "Foo"
let bar: Any = NSString(string: "Bar")

print(type(of: foo)) // String
print(type(of: bar)) // NSString

print(fooAsString is NSString) // false
print("foo" is NSString) // true

print(foo is NSString) // false
print(bar is NSString) // true

WARNINGS:
cast from 'String' to unrelated type 'NSString' always fails
print(fooAsString is NSString)

'is' test is always true
print("foo" is NSString)

Still, I believe that due to *SE-0072*
1) Linux version work more correctly and Mac's version should be fixed
2) There is a bug in Linux version, which produces
"foo" is NSString == true

Am I missing something?

There is no bug. Recall that "foo" is a string literal, which means that it
has no type of its own. Since NSString conforms to
ExpressibleByStringLiteral,"foo" is NSString == true because you are
testing if a value that's inferred to be of type NSString can be cast to
NSString. This does not have anything to do with bridging.

SE-0072 has to do with _implicit_ bridging from Objective-C to Swift (i.e.
NSString to String), and indeed you will find that implicit bridging in
that direction has been eliminated on a Mac:

import Foundation
let foo = "abc" as NSString
let bar: String = foo
// Error: NSString is not implicitly convertible to String [Fix-it: Insert
" as String"]

(Just got confused.. How should I cast NSString to String in Linux(or any)

version of Swift?

On Darwin platforms, you perform an explicit conversion by writing `foo as
String` (where foo is your NSString instance).
On Linux, it appears you need to write `foo._bridgeToSwift()` for the
moment.

Tried various variants like nsstring as! String and

···

On Thu, Aug 25, 2016 at 17:08 Vladimir.S via swift-evolution < swift-evolution@swift.org> wrote:

String(describing: nsstring) - got compilation/runtime error)

On 26.08.2016 0:31, Charles Srstka wrote:
>> On Aug 25, 2016, at 4:15 PM, Vladimir.S via swift-evolution > >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>> > let fooIsNSString = foo is NSString // true
>>
>> I don't understand. Why is so? On which version of Swift did you try
this?
>>
>> IBM Swift Sandbox
>> Swift Ver. 3.0 (Aug 23, 2016)
>> Platform: Linux (x86_64)
>>
>> import Foundation
>>
>> let foo: Any = "Foo"
>> let bar: Any = NSString(string: "Bar")
>>
>> let fooIsString = foo is String
>> print(fooIsString) // true
>>
>> let fooIsNSString = foo is NSString
>> print(fooIsNSString) // false! as expected
>
> The behavior is due to the Objective-C bridge, which I don’t think exists
> on the Linux version. On the Mac version, fooIsNSString will be true
> because of the bridging magic that lets you convert between String and
> NSString (and other equivalent Swift and Objective-C data types) using
> “as”. Never mind that “foo as NSString” isn’t even fewer characters than
> “NSString(foo)”; somebody decided it was easier that way. And of course,
> “is”, “as?” and the rest of the family have to behave the same way for
> consistency. As a result, you can never be 100% sure what’s actually
going
> to happen when you use “as?”, which is one of the reasons I dislike
“as?”.
> SE-0083 attempted to remove this bridging magic from the dynamic casts
and
> make them only truthfully report the type of the object or struct you
were
> looking at, but it was deferred until “later in Swift 3”, and Swift 3 has
> progressed past the point where source-breaking changes are accepted, so
> that proposal is probably dead at this point.
>
> The source incompatibility that this bridging behavior can create between
> Swift on the Mac and on other platforms is actually a pretty good point
in
> favor of SE-0083 that I wish we’d thought of before everything became
> locked down.
>
> Charles
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution