[swift-evolution] [Proposal] Type Narrowing

Dennis Lysenko dennis.s.lysenko at gmail.com
Sat Nov 12 20:37:15 CST 2016


That's a good point in Jay's example and from what I can tell a good way to
address it, Haravikk.

I've done some work in a language that only provides type narrowing for
immutable types (that'd be Kotlin as I've mentioned before) and whenever
I've used it it feels like the only thing it's really been missing is the
"if let" construct allowing you to bind and unwrap mutable values, which
leads me to think that synergistically, in Swift, this would be fantastic.
The main benefit probably is that it would allow code to read much better.

I think the "type stack" phrasing in the proposal is throwing some people
for a loop making them think it'll have way more mental overhead than it
actually does in practice...really, in practice, I've used this either (a)
for checking nullity or (b) for checking for a specific subclass. There's
little to no mental overhead in either of those cases, as your "type stack"
is simply 2-long (this was an Int? outside of this conditional block, and
it's an Int inside. This was a UIViewController outside of this conditional
block, and it's a UINavigationController inside.)

While it may not be a panacea that allows new applications that no one
could have thought of, I have no doubt that it would greatly improve the
experience of coding in the language by, as you said, allowing more
flexibility in expressiveness. Regardless of one's opinion on the efficacy
of this feature as a whole, there *are* frequent situations where this
feature leads to substantially better-reading code. The "unwrap" keyword
proposed in another thread, critically, solves only *half* of the  problems
that this proposal solves (as far as I could tell from reading a few emails
in the chain, it left the subclass inference completely untouched). Ability
to say "if (controller is SongSelectionViewController) {
controller.search(for: "mozart") }" is mentally freeing and helps me stay
in coder zen.

IMO, it would effectively be the cream cheese icing on the top of the
carrot cake of Swift's unwrapping and type inference features. A good
tasteful cream cheese icing always improves a carrot cake.

The question then becomes, is it really worth the implementation time? This
is something that, presumably, someone from the Swift team would need to be
involved in answering.

On Fri, Nov 11, 2016 at 10:52 AM Haravikk via swift-evolution <
swift-evolution at swift.org> wrote:

>
> On 10 Nov 2016, at 21:42, Jay Abbott <jay at abbott.me.uk> wrote:
>
> Consider this code:
>
> struct Pet {
>     let name: String
>     weak var owner: Person?
>
>     init(name: String, owner: Person?) {
>         self.name = name
>         self.owner = owner
>         owner?.pet = self
>     }
>
>     mutating func transferOwnership(to newOwner: Person) {
>         let previousOwner = owner
>         owner = newOwner
>         newOwner.pet = self
>         if(previousOwner != nil) {
>             previousOwner!.pet = nil
>         }
>     }
>
>     func feed() {
>     }
> }
> class Person {
>     let name: String
>     var pet: Pet?
>
>     init(name: String) {
>         self.name = name
>     }
>
>     func givePetAway(to someone: Person) {
>         if pet != nil {
>             pet!.transferOwnership(to: someone)
>             //pet!.feed()
>         }
>     }
> }
> let bert = Person(name: "Bert")let ernie = Person(name: "Ernie")var elmo = Pet(name: "Elmo", owner: nil)
>
> elmo.transferOwnership(to: bert)print("Bert's pet is \(bert.pet) - Ernie's pet is \(ernie.pet)")
>
> bert.givePetAway(to: ernie)print("Bert's pet is \(bert.pet) - Ernie's pet is \(ernie.pet)")
>
> This works as expected, but if you uncomment pet!.feed() in
> givePetAway(to:) it will crash, because the mutating function modifies
> the two-way relationship between pet and owner.
>
> In the code I use if pet != nil to demonstrate, in your proposal for
> unwrap, if I used it to unwrap pet (a value-type, but accessed through
> self so it can be modified after unwrapping because it’s not nil at the
> moment) the compiler would assume I could use it and pet.feed() would
> crash, just as pet!.feed() does now. In your proposal for type narrowing,
> it would be the same problem. This is like your foo.value example from
> the proposal.
>
> I don’t think you can get around the fact that the compiler can’t
> guarantee a type-narrowed or even unwrapped mutable value, which is why if
> let works as it does with an immutable snapshot.
>
> However, type narrowing for immutable values would still be good.
>
> This isn't a problem of mutability, it's a problem due to the use of a
> reference type (Person); this is what the classes and concurrency section
> is supposed to be describing, but perhaps I haven't made it clear enough.
> So in the example you've given self.pet can't be unwrapped because self is
> a reference type, thus you need to either use force unwrapping either with
> the unwrap! keyword or direct force unwrapping like you've used (since
> behind the scenes it's the same thing).
>
> You are right though that the resulting error isn't necessarily a
> concurrency issue, so I'll make a note of this for the proposed new error,
> and also clarify that the reference type restriction doesn't just apply to
> the property itself, but also to whatever it belongs to (and so-on up the
> chain, so if any part of foo.bar.a.b.c is a reference type it can't be soft
> unwrapped).
> _______________________________________________
> 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/20161113/0a346e63/attachment.html>


More information about the swift-evolution mailing list