> On 3 Nov 2016, at 22:56, Nevin Brackett-Rozinsky 
> <[email protected]> wrote:
> 
> Okay, I think I found an actual shortcoming that type narrowing might 
> address, namely mutating a property in place if it is a subtype. Here is a 
> small example setup:
> 
> protocol A { var x: Int {get} }
> struct B: A { var x: Int }
> struct C { var a: A }
> var c = C(a: B(x: 4))
> 
> Note that “A” does not promise the “x” property will be mutable, while B 
> does. I use “x: Int” as a minimal example, but any solution should also work 
> for more complex scenarios.
> 
> Now suppose we wish to test whether “c.a” is of type B, and if so change its 
> “x” value. We could, of course, make a local copy, mutate it, and reassign to 
> “c.a”. But if those operations are expensive we would rather avoid doing so. 
> And if B uses copy-on-write, we would want to remove the value from “c” 
> entirely so that we hopefully have a unique reference. This is hard to get 
> right.
> 
> We would prefer to write something like the following:
> 
> (c.a as? B)?.x = 12
> 
> But that does not currently work, resulting in the error “Cannot assign to 
> immutable expression of type 'Int'”.
> 
> Will the proposed type-narrowing feature provide a simple way to mutate “c.a” 
> in place, contingent upon its being of type B?
> How does it compare to an alternative such as inout return values, which 
> could preserve mutability in the above?

That's a good example, and yes it should be possible for type-narrowing to 
simplify stuff like this, I'll add a section on that I should probably go into 
more detail on how I intend working with narrowed mutable values to work, as 
for the advantage vs inout it's really just a matter of simplicity I think; 
using type narrowing should allow it to just work without having to pass to 
methods or design the API specifically for that kind of thing.



> If we are going to have any sort of type narrowing, I would strongly prefer 
> that it be explicit. For example we could use a keyword such as “rebind” to 
> narrow the type of an existing symbol in a scope:
> 
> if rebind c.a as B {
>     c.a.x = 12
> }

I don't see what's really gained by making it explicit vs implicit; if the 
condition was c.a is B then I'm not sure how that's any less clear?

> Furthermore, I think the proposal to treat enum cases as types is a major 
> change to Swift’s type system, and probably introduces many unforeseen 
> headaches. It also smells somewhat of a backdoor way for union types to sneak 
> into the language.

I'd say that in a sense enums are types, after all they can have unique 
associated value types of their own. Really though my intent is primarily to 
support optionals, but I figure it makes sense to try and do it from the 
perspective of general purpose enums since that's all optionals really are 
anyway.


Also, I've been thinking about thread safety and it occurs to me that type 
narrowing could actually be of significant benefit to it, rather than a 
detriment.

Consider:

        struct Foo { var value:Int }
        struct Bar { var foo?:Foo }

        var a = Foo(value: 5)
        var b = Bar(foo: a)

        b.foo.value = 10

Now, in this case we're only dealing with structs, and we know that b.foo has a 
value, thus b.foo.value is nice and safe; in fact no force unwrapping needs to 
occur behind the scenes, it can optimise away completely.

Instead, let's assume Bar is a class we're handling in a potentially shared way:

        class Bar { var foo?:Foo }

        func myMethod(bar:Bar) {
                b.foo = new Foo(5) // We now know that b.foo shouldn't be nil
                b.foo!.value = 10
        }

In this case, since Bar is a class we don't have total control over, we can't 
be certain that b.foo is non-nil when we try to modify it a second time; as a 
result, type-narrowing won't occur (because it's a class from another scope), 
however, because the type-checker knows that bar.foo should have a value, we 
can actually issue a more informative error message if force unwrapping fails, 
e.g- concurrent modification error.

In other words, we can potentially use it to help detect concurrency issues. 
It's another thing that's going to require a section on the proposal though, I 
have a feeling it's going to get pretty big!
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to