[swift-evolution] Pitch: really_is and really_as operators

Xiaodi Wu xiaodi.wu at gmail.com
Thu Aug 25 11:15:37 CDT 2016


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

>
> On Aug 25, 2016, at 9:37 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> 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 08:40 Matthew Johnson <matthew at anandabits.com>
> wrote:
>
>> On Aug 24, 2016, at 9:33 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>
>> On Wed, Aug 24, 2016 at 9:25 PM, Matthew Johnson via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>>
>>> On Aug 24, 2016, at 9:09 PM, Robert Widmann via swift-evolution <
>>> swift-evolution at 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 at 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 at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160825/46be5ef6/attachment.html>


More information about the swift-evolution mailing list