[swift-evolution] [Proposal] Type Narrowing

Haravikk swift-evolution at haravikk.me
Sun Nov 6 10:37:40 CST 2016


> On 3 Nov 2016, at 22:56, Nevin Brackett-Rozinsky <nevin.brackettrozinsky at gmail.com> 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!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161106/142f6771/attachment.html>


More information about the swift-evolution mailing list