[Draft] Throwing Properties and Subscripts

I've drafted a proposal to allow property and subscript accessors to throw errors:

  <https://github.com/brentdax/swift-evolution/blob/throwing-properties-draft/proposals/0000-throwing-properties.md>

I've left a lot of the rationale for particular design decisions for the "Alternatives considered" section, so please take a look at that before you ask, for instance, why this proposal doesn't import any Objective-C methods as throwing subscript accessors.

Throwing Properties and Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Brent Royal-Gordon <https://github.com/brentdax>
Status: Draft
Review manager: TBD
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#introduction>Introduction

Functions, methods, and initializers can be marked throws to indicate that they can fail by throwing an error, but properties and subscripts cannot. This proposal extends properties and subscripts to support throws and rethrows accessors, and also specifies logic for bridging these accessors to and from Objective-C.

Swift-evolution thread: Proposal: Allow Getters and Setters to Throw <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001165.html>
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#motivation>Motivation

Sometimes, something that is genuinely getter- or setter-like needs to be able to throw an error. This is particularly common with properties which convert between formats:

var image: UIImage

var imageData: NSData {
    get {
        return UIImagePNGRepresentation(image)
    }
    set {
        image = UIImage(data: newValue) ?? throw OopsICantThrowHere
    }
}
Or which access some external resource which may not be able to perform the operation:

var avatar: UIImage {
    get {
        let data = try NSData(contentsOfURL: avatarURL, options: []) /* can't try here! */
        return UIImage(data: data)
    }
}
The current best solution to this problem is to write a method instead of a property. This can lead to unnatural API designs; the class AVAudioSession alone, for instance, has no less than ten mismatched property/setter method pairs:

var category: String { get }
func setCategory(_ category: String) throws

var mode: String { get }
func setMode(_ mode: String) throws

var inputGain: Float { get }
func setInputGain(_ gain: Float) throws

var preferredSampleRate: Double { get }
func setPreferredSampleRate(_ sampleRate: Double) throws

var preferredIOBufferDuration: NSTimeInterval { get }
func setPreferredIOBufferDuration(_ duration: NSTimeInterval) throws

var preferredInputNumberOfChannels: Int { get }
func setPreferredInputNumberOfChannels(_ count: Int) throws

var preferredOutputNumberOfChannels: Int { get }
func setPreferredOutputNumberOfChannels(_ count: Int) throws

var preferredInput: AVAudioSessionPortDescription? { get }
func setPreferredInput(_ inPort: AVAudioSessionPortDescription?) throws

var inputDataSource: AVAudioSessionDataSourceDescription? { get }
func setInputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws

var outputDataSource: AVAudioSessionDataSourceDescription? { get }
func setOutputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws
While most classes aren't nearly this bad, you see the same problem elsewhere in the frameworks. The Mac-only CoreWLAN framework has similar mismatched property/setter method pairs (though it also has other bridging issues; I suspect it's too obscure to have been audited yet):

func wlanChannel() -> CWChannel!
func setWLANChannel(_ channel: CWChannel!, error error: NSErrorPointer) -> Bool

func powerOn() -> Bool
func setPower(_ power: Bool, error error: NSErrorPointer) -> Bool
When the getter can throw, it gets even worse. NSURL has an awkward pair of methods to get "resource values" which would be better expressed as a throwing read-write subscript:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
func setResourceValue(_ value: AnyObject?, forKey key: String) throws
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#proposed-solution>Proposed solution

Swift can handle these cases better by allowing getters and setters to throw.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-computed-properties>Throwing computed properties

You can mark a computed property accessor as throwing by putting throws after the get or set keyword:

var property: Int {
    get throws { ... }
    set throws { ... }
}

subscript(index: Int) -> Bool {
    get throws { ... }
    set throws { ... }
}
The throwing behavior of the getter and setter are completely independent; a throwing getter can be paired with a non-throwing setter, or vice versa.

var property: Int {
    get throws { ... }
    set { ... }
}

subscript(index: Int) -> Bool {
    get { ... }
    set throws { ... }
}
A protocol (or, if added later, an abstract class) can specify the throwing behavior of properties and subscripts it requires:

protocol MyProtocol {
    var property: Int { get throws set throws }
    subscript(index: Int) -> Bool { get throws set throws }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-stored-properties>Throwing stored properties

A stored property can also be given a throwing setter by giving it a willSet accessor that throws:

var property: Int {
    willSet throws {
        guard newValue >= 0 else {
            throw MyError.PropertyOutOfRange (newValue)
        }
    }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#importing-throwing-property-accessors-from-objective-c>Importing throwing property accessors from Objective-C

When a readonly property foo of type T is imported from Objective-C, but a method like this exists on the same type:

- (BOOL)setFoo:(T)value error:(NSError**)error;
Swift will import foo as a readwrite property with a throwing setter.

If SE-0044 Import as member <https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md> is accepted, we should also be able to apply the swift_name attribute to methods of these forms to create properties with throwing getters:

- (nullable T)foo:(NSError**)error; // property is not optional
- (BOOL)getFoo:(T*)outValue error:(NSError**)error;
No imports for throwing subscript accessors are specified.

These transformations should be applied to both classes and protocols.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#exporting-throwing-property-and-subscript-accessors-to-objective-c>Exporting throwing property and subscript accessors to Objective-C

A throwing setter for a property foo of type T should be exposed to Objective-C as:

- (void)setFoo:(T)value error:(NSError**)error;
A throwing getter for a property foo of type T, where T is not optional but can be nullable in Objective-C, should be exposed to Objective-C as:

- (nullable T)foo:(NSError**)error;
Otherwise, the getter should be exposed as:

- (BOOL)getFoo:(nonnull T*)outValue error:(NSError**)error;
A throwing setter for a subscript of type T with an index of type I, if marked with @objc(name), should be compatible with this signature:

- (BOOL)setFoo:(T)value atIndex:(I)index error:(NSError**)error;
A throwing getter for a subscript of type T with index I, where T is not optional but can be nullable in Objective-C, should be compatible with this signature:

- (nullable T)fooAtIndex:(I)index error:(NSError**)error;
Otherwise, the getter should be have a name compatible with this signature:

- (BOOL)getFoo:(nonnull T*)outValue atIndex:(I)index error:(NSError**)error;
Throwing subscript accessors which are not marked with @objc(name) will not be exposed to Objective-C.

These transformations should be applied to both classes and @objc protocols.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#detailed-design>Detailed design

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#subscripts-with-rethrows>Subscripts with rethrows

rethrows is not supported on properties, but it is supported on subscripts. The rethrowing behavior depends only on the subscript's parameters, not the setter's newValue; that is, a particular subscript access can throw iff at least one of the functions inside the square brackets can throw.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-accessors-and-inout-parameters>Throwing accessors and inout parameters

A throwing property or subscript access can be passed as an inout parameter. The call it is passed to must be marked with the try keyword.

To avoid unpredictable interactions between inout and throwing accessors, Swift will guarantee the getter is invoked once before the call and the setter once after the call. The compiler will not apply optimizations which might cause errors to be thrown in the middle of the function.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-requirement-compatibility>Throwing requirement compatibility

An implementation can be "less" throwing than a requirement it is intended to satisfy. That is:

A throwing accessor requirement can be fulfilled by a throwing, rethrowing, or non-throwing accessor.
A rethrowing accessor requirement can be fulfilled by a rethrowing or non-throwing accessor.
A non-throwing accessor requirement can be fulfilled only by a non-throwing accessor.
These definitions apply to protocol (and abstract class) conformance, subclass overrides, and library resilience. (Note that last point: Swift must permit an accessor to be made less throwing without breaking binary compatibility.)

When overriding a throwing accessor, the override must explicitly state the expected level of throwing behavior; omitting the keyword means the accessor is non-throwing. That is, in this example, Subclass.foo's setter is not automatically throws:

class Superclass {
    var foo: Int {
        willSet throws { ... }
    }
}

class Subclass: Superclass {
    override var foo: Int {
        set { try super.foo = newValue }
        // Error: nonthrowing setter includes throwing statement
    }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#implementation>Implementation

The internal materializeForSet is as throwing as the "most" throwing of get and set.

FIXME: Beyond that, I have no idea. Sorry. Please help me fill this out.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#impact-on-existing-code>Impact on existing code

Some APIs will be imported differently, breaking call sites. The Swift compiler will need to provide fix-it and migration support for these cases.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#alternatives-considered>Alternatives considered

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#require-setters-to-be-at-least-as-throwing-as-getters>Require setters to be at least as throwing as getters

Calling a setter often implicitly involves calling a getter, so it may make sense to require the setter to be at least as throwing as the getter. Absent feedback to this effect from implementors, however, my instinct is to leave them independent, as there may be use cases where a get can throw but a set cannot. (For instance, if an instance faults in data from an external source on demand, but only writes that data back when a save() call is invoked, get may throw if it's unable to fault in the data, but set would never need to throw.)

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#make-rethrows-setters-throwing-if-newvalue-is-throwing>Make rethrows setters throwing if newValue is throwing

newValue is sort of like a parameter to the setter, so it might technically be more consistent for rethrows to consider newValue when deciding if a particular invocation throws or not. However, I can't imagine a case where this would be appropriate behavior, and considering only the subscript parameters makes the getter and setter work better together.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#dont-include-willset-throws>Don't include willSet throws

The use of willSet throws to make a stored property throwing is a bit funky and could be omitted. I decided to include it because, if it does not exist, people will fake it with private properties anyway.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#include-didset-throws>Include didSet throws

There is no technical reason not to support didSet throws accessors, which would allow stored properties to be made throwing. However, this would usually be the wrong thing to do because it would leave the errant value in the property. If compelling use cases for it were cited, however, didSet throws could be added.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#permit-try-on-foo-itself-rather-than-the-call-using-it>Permit try on &foo itself, rather than the call using it

As specified, if foo has a throwing accessor and you want to pass it to a function bar with an inout parameter, you have to write this:

try bar(&foo)
In theory, we could instead allow you to mark only the & operator, leaving the rest of the expression uncovered by the try:

bar(try &foo)
This would make the source of the potential error more obvious, but it might make the semantics less clear, because try &foo can throw after the call is finished in addition to before. I judge the latter issue to be more serious.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#try-to-convert-keyed-gettersetter-methods-to-subscripts>Try to convert keyed getter/setter methods to subscripts

Swift could conceivably apply heuristics to discover Objective-C method pairs that can be expressed as subscripts. For instance, the NSURL method pair cited in the Motivation section:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
func setResourceValue(_ value: AnyObject?, forKey key: String) throws
Could be imported like this:

subscript (resourceValueFor key: String) -> AnyObject? {
    get throws
    set throws
}
There are several reasons not to do this:

There is no established pattern for throwing subscripts in Objective-C, so any we might establish would be mistake-prone.
SE-0044 <https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md> does not currently include subscripts, so there is no proposal pending which would allow the heuristic to be tweaked or the import logic to be invoked manually. (This is arguably an oversight in SE-0044.)
Many such cases would benefit from a human looking at the design. In the NSURL case, for instance, a human looking at the broader type might prefer a design like this:
var resourceValues: ResourceValues { get }

struct ResourceValues {
    subscript (key: String) -> AnyObject? {
        get throws { ... }
        set throws { ... }
    }

    func get(for keys: [String]) throws -> [String: AnyObject] { ... }
    func set(from dict: [String: AnyObject]) throws { ... }

    func removeCachedKeys() { ... }
    func removeCachedKey(key: String) { ... }
    func setTemporaryValue(_ value: AnyObject?, for key: String) { ... }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#automatically-export-throwing-subscript-accessors-to-objective-c>Automatically export throwing subscript accessors to Objective-C

Throwing subscript accessors can only be exported by specifying a name using an @objc property. It might be nice to export them by default, but Objective-C doesn't have an established pattern for throwing subscript accessors, so it's not clear how these methods would be named.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#add-a-nothrows-keyword>Add a nothrows keyword

Leaving an accessor's throwing behavior unspecified could make it automatically take on the behavior required by the type's superclass or conformed protocols. However, this would require a way to explicitly state that an accessor could notthrow, along the lines of the rarely-used but necessary nonmutating keyword.

I have chosen not to do this because Swift generally does not allow you to infer parts of a member's signature, and because I cannot come up with a way to spell this keyword that isn't ugly as sin.

···

--
Brent Royal-Gordon
Architechies

1 Like

Do you have an imagined use for throwing getters? Allowing the getter or setter to throw independently greatly complicates the abstract model for properties. While we don't have much in the way of abstraction tools over properties yet, it would be important to consider the impact this might have on "lens" functions that can perform mutable projections. Right now, you can more or less categorize mutable properties into two groups:

- "reference-like", projecting mutable storage indirectly through a reference, which you could think of as having a type (Base) -> inout Property. This includes not only class properties but `nonmutating` struct properties like `UnsafeMutablePointer.memory`.
- "value-like", projecting mutable storage that is part of a larger mutable value, which you could think of as having type (inout Base) -> inout Property. This includes most mutable struct properties that aren't explicitly `nonmutating`.

If properties can throw, we'd want to be able to represent that in the type system for projection functions. If there are four different ways a property can throw (no throws, only reads can throw, only writes can throw, or both reads and writes can throw), that gets really complicated.

-Joe

···

On Mar 13, 2016, at 8:07 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

I've drafted a proposal to allow property and subscript accessors to throw errors:

  <https://github.com/brentdax/swift-evolution/blob/throwing-properties-draft/proposals/0000-throwing-properties.md>

I've left a lot of the rationale for particular design decisions for the "Alternatives considered" section, so please take a look at that before you ask, for instance, why this proposal doesn't import any Objective-C methods as throwing subscript accessors.

Throwing Properties and Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Brent Royal-Gordon <https://github.com/brentdax>
Status: Draft
Review manager: TBD
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#introduction>Introduction

Functions, methods, and initializers can be marked throws to indicate that they can fail by throwing an error, but properties and subscripts cannot. This proposal extends properties and subscripts to support throws and rethrows accessors, and also specifies logic for bridging these accessors to and from Objective-C.

Swift-evolution thread: Proposal: Allow Getters and Setters to Throw <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001165.html>
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#motivation>Motivation

Sometimes, something that is genuinely getter- or setter-like needs to be able to throw an error. This is particularly common with properties which convert between formats:

var image: UIImage

var imageData: NSData {
    get {
        return UIImagePNGRepresentation(image)
    }
    set {
        image = UIImage(data: newValue) ?? throw OopsICantThrowHere
    }
}
Or which access some external resource which may not be able to perform the operation:

var avatar: UIImage {
    get {
        let data = try NSData(contentsOfURL: avatarURL, options: []) /* can't try here! */
        return UIImage(data: data)
    }
}
The current best solution to this problem is to write a method instead of a property. This can lead to unnatural API designs; the class AVAudioSession alone, for instance, has no less than ten mismatched property/setter method pairs:

var category: String { get }
func setCategory(_ category: String) throws

var mode: String { get }
func setMode(_ mode: String) throws

var inputGain: Float { get }
func setInputGain(_ gain: Float) throws

var preferredSampleRate: Double { get }
func setPreferredSampleRate(_ sampleRate: Double) throws

var preferredIOBufferDuration: NSTimeInterval { get }
func setPreferredIOBufferDuration(_ duration: NSTimeInterval) throws

var preferredInputNumberOfChannels: Int { get }
func setPreferredInputNumberOfChannels(_ count: Int) throws

var preferredOutputNumberOfChannels: Int { get }
func setPreferredOutputNumberOfChannels(_ count: Int) throws

var preferredInput: AVAudioSessionPortDescription? { get }
func setPreferredInput(_ inPort: AVAudioSessionPortDescription?) throws

var inputDataSource: AVAudioSessionDataSourceDescription? { get }
func setInputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws

var outputDataSource: AVAudioSessionDataSourceDescription? { get }
func setOutputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws
While most classes aren't nearly this bad, you see the same problem elsewhere in the frameworks. The Mac-only CoreWLAN framework has similar mismatched property/setter method pairs (though it also has other bridging issues; I suspect it's too obscure to have been audited yet):

func wlanChannel() -> CWChannel!
func setWLANChannel(_ channel: CWChannel!, error error: NSErrorPointer) -> Bool

func powerOn() -> Bool
func setPower(_ power: Bool, error error: NSErrorPointer) -> Bool
When the getter can throw, it gets even worse. NSURL has an awkward pair of methods to get "resource values" which would be better expressed as a throwing read-write subscript:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
func setResourceValue(_ value: AnyObject?, forKey key: String) throws
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#proposed-solution>Proposed solution

Swift can handle these cases better by allowing getters and setters to throw.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-computed-properties>Throwing computed properties

You can mark a computed property accessor as throwing by putting throws after the get or set keyword:

var property: Int {
    get throws { ... }
    set throws { ... }
}

subscript(index: Int) -> Bool {
    get throws { ... }
    set throws { ... }
}
The throwing behavior of the getter and setter are completely independent; a throwing getter can be paired with a non-throwing setter, or vice versa.

var property: Int {
    get throws { ... }
    set { ... }
}

subscript(index: Int) -> Bool {
    get { ... }
    set throws { ... }
}
A protocol (or, if added later, an abstract class) can specify the throwing behavior of properties and subscripts it requires:

protocol MyProtocol {
    var property: Int { get throws set throws }
    subscript(index: Int) -> Bool { get throws set throws }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-stored-properties>Throwing stored properties

A stored property can also be given a throwing setter by giving it a willSet accessor that throws:

var property: Int {
    willSet throws {
        guard newValue >= 0 else {
            throw MyError.PropertyOutOfRange (newValue)
        }
    }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#importing-throwing-property-accessors-from-objective-c>Importing throwing property accessors from Objective-C

When a readonly property foo of type T is imported from Objective-C, but a method like this exists on the same type:

- (BOOL)setFoo:(T)value error:(NSError**)error;
Swift will import foo as a readwrite property with a throwing setter.

If SE-0044 Import as member <https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md> is accepted, we should also be able to apply the swift_name attribute to methods of these forms to create properties with throwing getters:

- (nullable T)foo:(NSError**)error; // property is not optional
- (BOOL)getFoo:(T*)outValue error:(NSError**)error;
No imports for throwing subscript accessors are specified.

These transformations should be applied to both classes and protocols.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#exporting-throwing-property-and-subscript-accessors-to-objective-c>Exporting throwing property and subscript accessors to Objective-C

A throwing setter for a property foo of type T should be exposed to Objective-C as:

- (void)setFoo:(T)value error:(NSError**)error;
A throwing getter for a property foo of type T, where T is not optional but can be nullable in Objective-C, should be exposed to Objective-C as:

- (nullable T)foo:(NSError**)error;
Otherwise, the getter should be exposed as:

- (BOOL)getFoo:(nonnull T*)outValue error:(NSError**)error;
A throwing setter for a subscript of type T with an index of type I, if marked with @objc(name), should be compatible with this signature:

- (BOOL)setFoo:(T)value atIndex:(I)index error:(NSError**)error;
A throwing getter for a subscript of type T with index I, where T is not optional but can be nullable in Objective-C, should be compatible with this signature:

- (nullable T)fooAtIndex:(I)index error:(NSError**)error;
Otherwise, the getter should be have a name compatible with this signature:

- (BOOL)getFoo:(nonnull T*)outValue atIndex:(I)index error:(NSError**)error;
Throwing subscript accessors which are not marked with @objc(name) will not be exposed to Objective-C.

These transformations should be applied to both classes and @objc protocols.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#detailed-design>Detailed design

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#subscripts-with-rethrows>Subscripts with rethrows

rethrows is not supported on properties, but it is supported on subscripts. The rethrowing behavior depends only on the subscript's parameters, not the setter's newValue; that is, a particular subscript access can throw iff at least one of the functions inside the square brackets can throw.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-accessors-and-inout-parameters>Throwing accessors and inout parameters

A throwing property or subscript access can be passed as an inout parameter. The call it is passed to must be marked with the try keyword.

To avoid unpredictable interactions between inout and throwing accessors, Swift will guarantee the getter is invoked once before the call and the setter once after the call. The compiler will not apply optimizations which might cause errors to be thrown in the middle of the function.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-requirement-compatibility>Throwing requirement compatibility

An implementation can be "less" throwing than a requirement it is intended to satisfy. That is:

A throwing accessor requirement can be fulfilled by a throwing, rethrowing, or non-throwing accessor.
A rethrowing accessor requirement can be fulfilled by a rethrowing or non-throwing accessor.
A non-throwing accessor requirement can be fulfilled only by a non-throwing accessor.
These definitions apply to protocol (and abstract class) conformance, subclass overrides, and library resilience. (Note that last point: Swift must permit an accessor to be made less throwing without breaking binary compatibility.)

When overriding a throwing accessor, the override must explicitly state the expected level of throwing behavior; omitting the keyword means the accessor is non-throwing. That is, in this example, Subclass.foo's setter is not automatically throws:

class Superclass {
    var foo: Int {
        willSet throws { ... }
    }
}

class Subclass: Superclass {
    override var foo: Int {
        set { try super.foo = newValue }
        // Error: nonthrowing setter includes throwing statement
    }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#implementation>Implementation

The internal materializeForSet is as throwing as the "most" throwing of get and set.

FIXME: Beyond that, I have no idea. Sorry. Please help me fill this out.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#impact-on-existing-code>Impact on existing code

Some APIs will be imported differently, breaking call sites. The Swift compiler will need to provide fix-it and migration support for these cases.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#alternatives-considered>Alternatives considered

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#require-setters-to-be-at-least-as-throwing-as-getters>Require setters to be at least as throwing as getters

Calling a setter often implicitly involves calling a getter, so it may make sense to require the setter to be at least as throwing as the getter. Absent feedback to this effect from implementors, however, my instinct is to leave them independent, as there may be use cases where a get can throw but a set cannot. (For instance, if an instance faults in data from an external source on demand, but only writes that data back when a save() call is invoked, get may throw if it's unable to fault in the data, but set would never need to throw.)

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#make-rethrows-setters-throwing-if-newvalue-is-throwing>Make rethrows setters throwing if newValue is throwing

newValue is sort of like a parameter to the setter, so it might technically be more consistent for rethrows to consider newValue when deciding if a particular invocation throws or not. However, I can't imagine a case where this would be appropriate behavior, and considering only the subscript parameters makes the getter and setter work better together.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#dont-include-willset-throws>Don't include willSet throws

The use of willSet throws to make a stored property throwing is a bit funky and could be omitted. I decided to include it because, if it does not exist, people will fake it with private properties anyway.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#include-didset-throws>Include didSet throws

There is no technical reason not to support didSet throws accessors, which would allow stored properties to be made throwing. However, this would usually be the wrong thing to do because it would leave the errant value in the property. If compelling use cases for it were cited, however, didSet throws could be added.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#permit-try-on-foo-itself-rather-than-the-call-using-it>Permit try on &foo itself, rather than the call using it

As specified, if foo has a throwing accessor and you want to pass it to a function bar with an inout parameter, you have to write this:

try bar(&foo)
In theory, we could instead allow you to mark only the & operator, leaving the rest of the expression uncovered by the try:

bar(try &foo)
This would make the source of the potential error more obvious, but it might make the semantics less clear, because try &foo can throw after the call is finished in addition to before. I judge the latter issue to be more serious.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#try-to-convert-keyed-gettersetter-methods-to-subscripts>Try to convert keyed getter/setter methods to subscripts

Swift could conceivably apply heuristics to discover Objective-C method pairs that can be expressed as subscripts. For instance, the NSURL method pair cited in the Motivation section:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
func setResourceValue(_ value: AnyObject?, forKey key: String) throws
Could be imported like this:

subscript (resourceValueFor key: String) -> AnyObject? {
    get throws
    set throws
}
There are several reasons not to do this:

There is no established pattern for throwing subscripts in Objective-C, so any we might establish would be mistake-prone.
SE-0044 <https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md> does not currently include subscripts, so there is no proposal pending which would allow the heuristic to be tweaked or the import logic to be invoked manually. (This is arguably an oversight in SE-0044.)
Many such cases would benefit from a human looking at the design. In the NSURL case, for instance, a human looking at the broader type might prefer a design like this:
var resourceValues: ResourceValues { get }

struct ResourceValues {
    subscript (key: String) -> AnyObject? {
        get throws { ... }
        set throws { ... }
    }

    func get(for keys: [String]) throws -> [String: AnyObject] { ... }
    func set(from dict: [String: AnyObject]) throws { ... }

    func removeCachedKeys() { ... }
    func removeCachedKey(key: String) { ... }
    func setTemporaryValue(_ value: AnyObject?, for key: String) { ... }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#automatically-export-throwing-subscript-accessors-to-objective-c>Automatically export throwing subscript accessors to Objective-C

Throwing subscript accessors can only be exported by specifying a name using an @objc property. It might be nice to export them by default, but Objective-C doesn't have an established pattern for throwing subscript accessors, so it's not clear how these methods would be named.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#add-a-nothrows-keyword>Add a nothrows keyword

Leaving an accessor's throwing behavior unspecified could make it automatically take on the behavior required by the type's superclass or conformed protocols. However, this would require a way to explicitly state that an accessor could notthrow, along the lines of the rarely-used but necessary nonmutating keyword.

I have chosen not to do this because Swift generally does not allow you to infer parts of a member's signature, and because I cannot come up with a way to spell this keyword that isn't ugly as sin.

--
Brent Royal-Gordon
Architechies

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

Nicely written!

I wonder if it would help to document the grammar changes necessary in the proposal.

I also wonder how it could affect the getter shorthand? Couldn't the following work for subscripts?

    subscript(idx: Int) throws -> Element { ... }

    // the above is shorthand for
    subscript(idx: Int) -> Element {
        get throws { ... }
    }

Not sure how this could be extended to work with general computed vars. This doesn't feel right:

    var x throws: Int { ... }

Stephen

···

On Mar 13, 2016, at 11:07 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

I've drafted a proposal to allow property and subscript accessors to throw errors:

  <https://github.com/brentdax/swift-evolution/blob/throwing-properties-draft/proposals/0000-throwing-properties.md>

I've left a lot of the rationale for particular design decisions for the "Alternatives considered" section, so please take a look at that before you ask, for instance, why this proposal doesn't import any Objective-C methods as throwing subscript accessors.

Throwing Properties and Subscripts

  • Proposal: SE-NNNN
  • Author(s): Brent Royal-Gordon
  • Status: Draft
  • Review manager: TBD
Introduction

Functions, methods, and initializers can be marked throws to indicate that they can fail by throwing an error, but properties and subscripts cannot. This proposal extends properties and subscripts to support throws and rethrows accessors, and also specifies logic for bridging these accessors to and from Objective-C.

Swift-evolution thread: Proposal: Allow Getters and Setters to Throw

Motivation

Sometimes, something that is genuinely getter- or setter-like needs to be able to throw an error. This is particularly common with properties which convert between formats:

var
image: UIImage

var
imageData: NSData {
    
get
{
        
return
UIImagePNGRepresentation(image)
    }
    
set
{
        image
= UIImage(data: newValue) ?? throw
OopsICantThrowHere
    }
}

Or which access some external resource which may not be able to perform the operation:

var
avatar: UIImage {
    
get
{
        
let data = try NSData(contentsOfURL: avatarURL, options: []) /* can't try here! */

return
UIImage(data: data)
    }
}

The current best solution to this problem is to write a method instead of a property. This can lead to unnatural API designs; the class AVAudioSession alone, for instance, has no less than ten mismatched property/setter method pairs:

var category: String { get
}

func setCategory(_ category: String
) throws

var mode: String { get
}

func setMode(_ mode: String
) throws

var inputGain: Float { get
}

func setInputGain(_ gain: Float
) throws

var preferredSampleRate: Double { get
}

func setPreferredSampleRate(_ sampleRate: Double
) throws

var preferredIOBufferDuration: NSTimeInterval { get
}

func setPreferredIOBufferDuration(_ duration
: NSTimeInterval) throws

var preferredInputNumberOfChannels: Int { get
}

func setPreferredInputNumberOfChannels(_ count: Int
) throws

var preferredOutputNumberOfChannels: Int { get
}

func setPreferredOutputNumberOfChannels(_ count: Int
) throws

var preferredInput: AVAudioSessionPortDescription? { get
}

func setPreferredInput(_ inPort
: AVAudioSessionPortDescription?) throws

var inputDataSource: AVAudioSessionDataSourceDescription? { get
}

func setInputDataSource(_ dataSource
: AVAudioSessionDataSourceDescription?) throws

var outputDataSource: AVAudioSessionDataSourceDescription? { get
}

func setOutputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws
While most classes aren't nearly this bad, you see the same problem elsewhere in the frameworks. The Mac-only CoreWLAN framework has similar mismatched property/setter method pairs (though it also has other bridging issues; I suspect it's too obscure to have been audited yet):

func wlanChannel() ->
CWChannel!

func setWLANChannel(_ channel: CWChannel!, error error: NSErrorPointer) -> Bool

func powerOn() -> Bool
func setPower(_ power: Bool, error error: NSErrorPointer) -> Bool
When the getter can throw, it gets even worse. NSURL has an awkward pair of methods to get "resource values" which would be better expressed as a throwing read-write subscript:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String
) throws

func setResourceValue(_ value: AnyObject?, forKey key: String) throws
Proposed solution

Swift can handle these cases better by allowing getters and setters to throw.

Throwing computed properties

You can mark a computed property accessor as throwing by putting throws after the get or set keyword:

var property: Int
{
    
get throws { ...
}
    
set throws { ...
}
}

subscript(index: Int) -> Bool
{
    
get throws { ...
}
    
set throws { ...
}
}

The throwing behavior of the getter and setter are completely independent; a throwing getter can be paired with a non-throwing setter, or vice versa.

var property: Int
{
    
get throws { ...
}
    
set { ...
}
}

subscript(index: Int) -> Bool
{
    
get { ...
}
    
set throws { ...
}
}

A protocol (or, if added later, an abstract class) can specify the throwing behavior of properties and subscripts it requires:

protocol
MyProtocol {
    
var property: Int { get throws set throws
}
    
subscript(index: Int) -> Bool { get throws set throws
}
}

Throwing stored properties

A stored property can also be given a throwing setter by giving it a willSet accessor that throws:

var property: Int
{
    
willSet throws
{
        
guard newValue >= 0 else
{
            
throw MyError.
PropertyOutOfRange (newValue)
        }
    }
}

Importing throwing property accessors from Objective-C

When a readonly property foo of type T is imported from Objective-C, but a method like this exists on the same type:

- (BOOL)setFoo:(T)value error:(NSError**)error;
Swift will import foo as a readwrite property with a throwing setter.

If SE-0044 Import as member is accepted, we should also be able to apply the swift_name attribute to methods of these forms to create properties with throwing getters:

- (nullable T)foo:(NSError**)error; // property is not optional

- (
BOOL)getFoo:(T*)outValue error:(NSError**)error;
No imports for throwing subscript accessors are specified.

These transformations should be applied to both classes and protocols.

Exporting throwing property and subscript accessors to Objective-C

A throwing setter for a property foo of type T should be exposed to Objective-C as:

- (void)setFoo:(T)value error:(NSError**)error;
A throwing getter for a property foo of type T, where T is not optional but can be nullable in Objective-C, should be exposed to Objective-C as:

- (nullable T)foo:(NSError**)error;
Otherwise, the getter should be exposed as:

- (BOOL)getFoo:(nonnull T*)outValue error:(NSError**)error;
A throwing setter for a subscript of type T with an index of type I, if marked with @objc(name), should be compatible with this signature:

- (BOOL)setFoo:(T)value atIndex:(I)index error:(NSError**)error;
A throwing getter for a subscript of type T with index I, where T is not optional but can be nullable in Objective-C, should be compatible with this signature:

- (nullable T)fooAtIndex:(I)index error:(NSError**)error;
Otherwise, the getter should be have a name compatible with this signature:

- (BOOL)getFoo:(nonnull T*)outValue atIndex:(I)index error:(NSError**)error;
Throwing subscript accessors which are not marked with @objc(name) will not be exposed to Objective-C.

These transformations should be applied to both classes and @objc protocols.

Detailed design

Subscripts with rethrows

rethrows is not supported on properties, but it is supported on subscripts. The rethrowing behavior depends only on the subscript's parameters, not the setter's newValue; that is, a particular subscript access can throw iff at least one of the functions inside the square brackets can throw.

Throwing accessors and inout parameters

A throwing property or subscript access can be passed as an inout parameter. The call it is passed to must be marked with the try keyword.

To avoid unpredictable interactions between inout and throwing accessors, Swift will guarantee the getter is invoked once before the call and the setter once after the call. The compiler will not apply optimizations which might cause errors to be thrown in the middle of the function.

Throwing requirement compatibility

An implementation can be "less" throwing than a requirement it is intended to satisfy. That is:

  • A throwing accessor requirement can be fulfilled by a throwing, rethrowing, or non-throwing accessor.
  • A rethrowing accessor requirement can be fulfilled by a rethrowing or non-throwing accessor.
  • A non-throwing accessor requirement can be fulfilled only by a non-throwing accessor.
These definitions apply to protocol (and abstract class) conformance, subclass overrides, and library resilience. (Note that last point: Swift must permit an accessor to be made less throwing without breaking binary compatibility.)

When overriding a throwing accessor, the override must explicitly state the expected level of throwing behavior; omitting the keyword means the accessor is non-throwing. That is, in this example, Subclass.foo's setter is not automatically throws:

class Superclass {
    var foo: Int {
        willSet throws { ... }
    }
}

class Subclass: Superclass {
    override var foo: Int {
        set { try super.foo = newValue }
        // Error: nonthrowing setter includes throwing statement
    }
}

Implementation

The internal materializeForSet is as throwing as the "most" throwing of get and set.

FIXME: Beyond that, I have no idea. Sorry. Please help me fill this out.

Impact on existing code

Some APIs will be imported differently, breaking call sites. The Swift compiler will need to provide fix-it and migration support for these cases.

Alternatives considered

Require setters to be at least as throwing as getters

Calling a setter often implicitly involves calling a getter, so it may make sense to require the setter to be at least as throwing as the getter. Absent feedback to this effect from implementors, however, my instinct is to leave them independent, as there may be use cases where a get can throw but a set cannot. (For instance, if an instance faults in data from an external source on demand, but only writes that data back when a save() call is invoked, get may throw if it's unable to fault in the data, but set would never need to throw.)

Make rethrows setters throwing if newValue is throwing

newValue is sort of like a parameter to the setter, so it might technically be more consistent for rethrows to consider newValue when deciding if a particular invocation throws or not. However, I can't imagine a case where this would be appropriate behavior, and considering only the subscript parameters makes the getter and setter work better together.

Don't include willSet throws

The use of willSet throws to make a stored property throwing is a bit funky and could be omitted. I decided to include it because, if it does not exist, people will fake it with private properties anyway.

Include didSet throws

There is no technical reason not to support didSet throws accessors, which would allow stored properties to be made throwing. However, this would usually be the wrong thing to do because it would leave the errant value in the property. If compelling use cases for it were cited, however, didSet throws could be added.

Permit try on &foo itself, rather than the call using it

As specified, if foo has a throwing accessor and you want to pass it to a function bar with an inout parameter, you have to write this:

try bar(&foo)

In theory, we could instead allow you to mark only the & operator, leaving the rest of the expression uncovered by the try:

bar(try &foo)

This would make the source of the potential error more obvious, but it might make the semantics less clear, because try &foo can throw after the call is finished in addition to before. I judge the latter issue to be more serious.

Try to convert keyed getter/setter methods to subscripts

Swift could conceivably apply heuristics to discover Objective-C method pairs that can be expressed as subscripts. For instance, the NSURL method pair cited in the Motivation section:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String
) throws

func setResourceValue(_ value: AnyObject?, forKey key: String) throws
Could be imported like this:

subscript (resourceValueFor key: String) -> AnyObject
? {
    
get throws

set throws

}

There are several reasons not to do this:

  • There is no established pattern for throwing subscripts in Objective-C, so any we might establish would be mistake-prone.
  • SE-0044 does not currently include subscripts, so there is no proposal pending which would allow the heuristic to be tweaked or the import logic to be invoked manually. (This is arguably an oversight in SE-0044.)
  • Many such cases would benefit from a human looking at the design. In the NSURL case, for instance, a human looking at the broader type might prefer a design like this:
var resourceValues: ResourceValues { get
}

struct
ResourceValues {
    
subscript (key: String) -> AnyObject
? {
        
get throws { ...
}
        
set throws { ...
}
    }

func get(for keys: [String]) throws -> [String: AnyObject] { ...
}
    
func set(from dict: [String: AnyObject]) throws { ...
}

func removeCachedKeys() { ...
}
    
func removeCachedKey(key: String) { ...
}
    
func setTemporaryValue(_ value: AnyObject?, for key: String) { ...
}
}

Automatically export throwing subscript accessors to Objective-C

Throwing subscript accessors can only be exported by specifying a name using an @objc property. It might be nice to export them by default, but Objective-C doesn't have an established pattern for throwing subscript accessors, so it's not clear how these methods would be named.

Add a nothrows keyword

Leaving an accessor's throwing behavior unspecified could make it automatically take on the behavior required by the type's superclass or conformed protocols. However, this would require a way to explicitly state that an accessor could notthrow, along the lines of the rarely-used but necessary nonmutating keyword.

I have chosen not to do this because Swift generally does not allow you to infer parts of a member's signature, and because I cannot come up with a way to spell this keyword that isn't ugly as sin.

--
Brent Royal-Gordon
Architechies

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

Beautiful! Big +1 here

···

On Mar 13, 2016, at 11:07 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

I've drafted a proposal to allow property and subscript accessors to throw errors:

  <https://github.com/brentdax/swift-evolution/blob/throwing-properties-draft/proposals/0000-throwing-properties.md>

I've left a lot of the rationale for particular design decisions for the "Alternatives considered" section, so please take a look at that before you ask, for instance, why this proposal doesn't import any Objective-C methods as throwing subscript accessors.

Throwing Properties and Subscripts

Proposal: SE-NNNN <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Brent Royal-Gordon <https://github.com/brentdax>
Status: Draft
Review manager: TBD
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#introduction>Introduction

Functions, methods, and initializers can be marked throws to indicate that they can fail by throwing an error, but properties and subscripts cannot. This proposal extends properties and subscripts to support throws and rethrows accessors, and also specifies logic for bridging these accessors to and from Objective-C.

Swift-evolution thread: Proposal: Allow Getters and Setters to Throw <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001165.html>
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#motivation>Motivation

Sometimes, something that is genuinely getter- or setter-like needs to be able to throw an error. This is particularly common with properties which convert between formats:

var image: UIImage

var imageData: NSData {
    get {
        return UIImagePNGRepresentation(image)
    }
    set {
        image = UIImage(data: newValue) ?? throw OopsICantThrowHere
    }
}
Or which access some external resource which may not be able to perform the operation:

var avatar: UIImage {
    get {
        let data = try NSData(contentsOfURL: avatarURL, options: []) /* can't try here! */
        return UIImage(data: data)
    }
}
The current best solution to this problem is to write a method instead of a property. This can lead to unnatural API designs; the class AVAudioSession alone, for instance, has no less than ten mismatched property/setter method pairs:

var category: String { get }
func setCategory(_ category: String) throws

var mode: String { get }
func setMode(_ mode: String) throws

var inputGain: Float { get }
func setInputGain(_ gain: Float) throws

var preferredSampleRate: Double { get }
func setPreferredSampleRate(_ sampleRate: Double) throws

var preferredIOBufferDuration: NSTimeInterval { get }
func setPreferredIOBufferDuration(_ duration: NSTimeInterval) throws

var preferredInputNumberOfChannels: Int { get }
func setPreferredInputNumberOfChannels(_ count: Int) throws

var preferredOutputNumberOfChannels: Int { get }
func setPreferredOutputNumberOfChannels(_ count: Int) throws

var preferredInput: AVAudioSessionPortDescription? { get }
func setPreferredInput(_ inPort: AVAudioSessionPortDescription?) throws

var inputDataSource: AVAudioSessionDataSourceDescription? { get }
func setInputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws

var outputDataSource: AVAudioSessionDataSourceDescription? { get }
func setOutputDataSource(_ dataSource: AVAudioSessionDataSourceDescription?) throws
While most classes aren't nearly this bad, you see the same problem elsewhere in the frameworks. The Mac-only CoreWLAN framework has similar mismatched property/setter method pairs (though it also has other bridging issues; I suspect it's too obscure to have been audited yet):

func wlanChannel() -> CWChannel!
func setWLANChannel(_ channel: CWChannel!, error error: NSErrorPointer) -> Bool

func powerOn() -> Bool
func setPower(_ power: Bool, error error: NSErrorPointer) -> Bool
When the getter can throw, it gets even worse. NSURL has an awkward pair of methods to get "resource values" which would be better expressed as a throwing read-write subscript:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
func setResourceValue(_ value: AnyObject?, forKey key: String) throws
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#proposed-solution>Proposed solution

Swift can handle these cases better by allowing getters and setters to throw.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-computed-properties>Throwing computed properties

You can mark a computed property accessor as throwing by putting throws after the get or set keyword:

var property: Int {
    get throws { ... }
    set throws { ... }
}

subscript(index: Int) -> Bool {
    get throws { ... }
    set throws { ... }
}
The throwing behavior of the getter and setter are completely independent; a throwing getter can be paired with a non-throwing setter, or vice versa.

var property: Int {
    get throws { ... }
    set { ... }
}

subscript(index: Int) -> Bool {
    get { ... }
    set throws { ... }
}
A protocol (or, if added later, an abstract class) can specify the throwing behavior of properties and subscripts it requires:

protocol MyProtocol {
    var property: Int { get throws set throws }
    subscript(index: Int) -> Bool { get throws set throws }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-stored-properties>Throwing stored properties

A stored property can also be given a throwing setter by giving it a willSet accessor that throws:

var property: Int {
    willSet throws {
        guard newValue >= 0 else {
            throw MyError.PropertyOutOfRange (newValue)
        }
    }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#importing-throwing-property-accessors-from-objective-c>Importing throwing property accessors from Objective-C

When a readonly property foo of type T is imported from Objective-C, but a method like this exists on the same type:

- (BOOL)setFoo:(T)value error:(NSError**)error;
Swift will import foo as a readwrite property with a throwing setter.

If SE-0044 Import as member <https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md> is accepted, we should also be able to apply the swift_name attribute to methods of these forms to create properties with throwing getters:

- (nullable T)foo:(NSError**)error; // property is not optional
- (BOOL)getFoo:(T*)outValue error:(NSError**)error;
No imports for throwing subscript accessors are specified.

These transformations should be applied to both classes and protocols.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#exporting-throwing-property-and-subscript-accessors-to-objective-c>Exporting throwing property and subscript accessors to Objective-C

A throwing setter for a property foo of type T should be exposed to Objective-C as:

- (void)setFoo:(T)value error:(NSError**)error;
A throwing getter for a property foo of type T, where T is not optional but can be nullable in Objective-C, should be exposed to Objective-C as:

- (nullable T)foo:(NSError**)error;
Otherwise, the getter should be exposed as:

- (BOOL)getFoo:(nonnull T*)outValue error:(NSError**)error;
A throwing setter for a subscript of type T with an index of type I, if marked with @objc(name), should be compatible with this signature:

- (BOOL)setFoo:(T)value atIndex:(I)index error:(NSError**)error;
A throwing getter for a subscript of type T with index I, where T is not optional but can be nullable in Objective-C, should be compatible with this signature:

- (nullable T)fooAtIndex:(I)index error:(NSError**)error;
Otherwise, the getter should be have a name compatible with this signature:

- (BOOL)getFoo:(nonnull T*)outValue atIndex:(I)index error:(NSError**)error;
Throwing subscript accessors which are not marked with @objc(name) will not be exposed to Objective-C.

These transformations should be applied to both classes and @objc protocols.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#detailed-design>Detailed design

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#subscripts-with-rethrows>Subscripts with rethrows

rethrows is not supported on properties, but it is supported on subscripts. The rethrowing behavior depends only on the subscript's parameters, not the setter's newValue; that is, a particular subscript access can throw iff at least one of the functions inside the square brackets can throw.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-accessors-and-inout-parameters>Throwing accessors and inout parameters

A throwing property or subscript access can be passed as an inout parameter. The call it is passed to must be marked with the try keyword.

To avoid unpredictable interactions between inout and throwing accessors, Swift will guarantee the getter is invoked once before the call and the setter once after the call. The compiler will not apply optimizations which might cause errors to be thrown in the middle of the function.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#throwing-requirement-compatibility>Throwing requirement compatibility

An implementation can be "less" throwing than a requirement it is intended to satisfy. That is:

A throwing accessor requirement can be fulfilled by a throwing, rethrowing, or non-throwing accessor.
A rethrowing accessor requirement can be fulfilled by a rethrowing or non-throwing accessor.
A non-throwing accessor requirement can be fulfilled only by a non-throwing accessor.
These definitions apply to protocol (and abstract class) conformance, subclass overrides, and library resilience. (Note that last point: Swift must permit an accessor to be made less throwing without breaking binary compatibility.)

When overriding a throwing accessor, the override must explicitly state the expected level of throwing behavior; omitting the keyword means the accessor is non-throwing. That is, in this example, Subclass.foo's setter is not automatically throws:

class Superclass {
    var foo: Int {
        willSet throws { ... }
    }
}

class Subclass: Superclass {
    override var foo: Int {
        set { try super.foo = newValue }
        // Error: nonthrowing setter includes throwing statement
    }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#implementation>Implementation

The internal materializeForSet is as throwing as the "most" throwing of get and set.

FIXME: Beyond that, I have no idea. Sorry. Please help me fill this out.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#impact-on-existing-code>Impact on existing code

Some APIs will be imported differently, breaking call sites. The Swift compiler will need to provide fix-it and migration support for these cases.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#alternatives-considered>Alternatives considered

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#require-setters-to-be-at-least-as-throwing-as-getters>Require setters to be at least as throwing as getters

Calling a setter often implicitly involves calling a getter, so it may make sense to require the setter to be at least as throwing as the getter. Absent feedback to this effect from implementors, however, my instinct is to leave them independent, as there may be use cases where a get can throw but a set cannot. (For instance, if an instance faults in data from an external source on demand, but only writes that data back when a save() call is invoked, get may throw if it's unable to fault in the data, but set would never need to throw.)

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#make-rethrows-setters-throwing-if-newvalue-is-throwing>Make rethrows setters throwing if newValue is throwing

newValue is sort of like a parameter to the setter, so it might technically be more consistent for rethrows to consider newValue when deciding if a particular invocation throws or not. However, I can't imagine a case where this would be appropriate behavior, and considering only the subscript parameters makes the getter and setter work better together.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#dont-include-willset-throws>Don't include willSet throws

The use of willSet throws to make a stored property throwing is a bit funky and could be omitted. I decided to include it because, if it does not exist, people will fake it with private properties anyway.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#include-didset-throws>Include didSet throws

There is no technical reason not to support didSet throws accessors, which would allow stored properties to be made throwing. However, this would usually be the wrong thing to do because it would leave the errant value in the property. If compelling use cases for it were cited, however, didSet throws could be added.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#permit-try-on-foo-itself-rather-than-the-call-using-it>Permit try on &foo itself, rather than the call using it

As specified, if foo has a throwing accessor and you want to pass it to a function bar with an inout parameter, you have to write this:

try bar(&foo)
In theory, we could instead allow you to mark only the & operator, leaving the rest of the expression uncovered by the try:

bar(try &foo)
This would make the source of the potential error more obvious, but it might make the semantics less clear, because try &foo can throw after the call is finished in addition to before. I judge the latter issue to be more serious.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#try-to-convert-keyed-gettersetter-methods-to-subscripts>Try to convert keyed getter/setter methods to subscripts

Swift could conceivably apply heuristics to discover Objective-C method pairs that can be expressed as subscripts. For instance, the NSURL method pair cited in the Motivation section:

func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
func setResourceValue(_ value: AnyObject?, forKey key: String) throws
Could be imported like this:

subscript (resourceValueFor key: String) -> AnyObject? {
    get throws
    set throws
}
There are several reasons not to do this:

There is no established pattern for throwing subscripts in Objective-C, so any we might establish would be mistake-prone.
SE-0044 <https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md> does not currently include subscripts, so there is no proposal pending which would allow the heuristic to be tweaked or the import logic to be invoked manually. (This is arguably an oversight in SE-0044.)
Many such cases would benefit from a human looking at the design. In the NSURL case, for instance, a human looking at the broader type might prefer a design like this:
var resourceValues: ResourceValues { get }

struct ResourceValues {
    subscript (key: String) -> AnyObject? {
        get throws { ... }
        set throws { ... }
    }

    func get(for keys: [String]) throws -> [String: AnyObject] { ... }
    func set(from dict: [String: AnyObject]) throws { ... }

    func removeCachedKeys() { ... }
    func removeCachedKey(key: String) { ... }
    func setTemporaryValue(_ value: AnyObject?, for key: String) { ... }
}
<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#automatically-export-throwing-subscript-accessors-to-objective-c>Automatically export throwing subscript accessors to Objective-C

Throwing subscript accessors can only be exported by specifying a name using an @objc property. It might be nice to export them by default, but Objective-C doesn't have an established pattern for throwing subscript accessors, so it's not clear how these methods would be named.

<https://github.com/brentdax/swift-evolution/tree/throwing-properties-draft#add-a-nothrows-keyword>Add a nothrows keyword

Leaving an accessor's throwing behavior unspecified could make it automatically take on the behavior required by the type's superclass or conformed protocols. However, this would require a way to explicitly state that an accessor could notthrow, along the lines of the rarely-used but necessary nonmutating keyword.

I have chosen not to do this because Swift generally does not allow you to infer parts of a member's signature, and because I cannot come up with a way to spell this keyword that isn't ugly as sin.

--
Brent Royal-Gordon
Architechies

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

This is an interesting proposal.

I have a couple of throwing `setSomething` methods in my code that could be expressed as throwing property setters. That said, I don't really mind them being methods. Methods having a verb in their names makes them fit well with `try` at the call site.

I also have a getter returning nil when it fails to retrieve its data, in which case an error becomes available in a separate `error` property. That would seem like an ideal case for a throwing getter... but I'm not too sure I'd use the feature in this case because if an error occurs in this part of the code I actually want to make that error become part of the state of the object, stored and available separately for use at a later point. Basically a non-interrupting error. So I find my current approach is actually quite fitting and I don't feel a throwing getter is something I really need. Perhaps this is something particular to my use case, but if I needed it to throw I'd use a method and it'd be perfectly fine.

All that to say I'm pretty much neutral on this feature. To me it does not really add capabilities to the language, it just makes possible to do with properties certain things you can already do with methods, and I'm perfectly willing to use methods for things that can throw. But if properties could throw, especially the setter, I'd probably use the feature at some places.

And whatever we end up with for properties, I feel subscripts should work the same. Subscripts are just parametrized properties.

···

--
Michel Fortin
https://michelf.ca

Brent Royal-Gordon via swift-evolution <swift-evolution@...> writes:

Hi Brent, Excellent work! That is an extremely well written proposal draft - and
I would love to see the proposal accepted.

Here are my 2 cents on the discussion about alternatives:

1. Omit throwing getters on properties:

Throwing getters were actual my original use case for something like this
language feature.

I think that your argument about symmetry is excellent. It would be a bad thing
to have to split a calculated property into a throwing getter method and a
throwing setter method in case the getter needs to be able to throw.

While a throwing calculated read only property could of course be remodelled as
a throwing method, I still think that it is up to the developer to decide how a
concept should be exposed in an API.

It would also conceptually be hard to grasp why a throwing setter could exist in
a calculated property while a throwing getter could not. I think this would make
the language harder to learn.

So I would definitely include both throwing getters and setters in your
proposal.

2. Require setters to be at least as throwing as getters

I agree with you that these should be completely independent. Again for the
reason that the interdependence would be a hard thing to grasp - and you have
already come up with use cases where it would be nice to have the independence.

3. Don't include willSet throws

I would personally leave this out of the proposal. In the Swift Programming
Language documentation 'willSet' and 'didSet' are described as 'Property
Observers'. To me, conceptually, this means that they can perform code based on
the observed value change, but they can only observe - not cause the setting to
not happen in the first place (if that is a fair view of throwing in willSet).

4. Include didSet throws

For the same reason as above I think that this should not be included.

5. Permit try on &foo itself, rather than the call using it

If we were not talking about an inout parameter, then I think 'try' on the
property getter itself should be allowed.

Today - if you have a throwing function that returns a value, then you can do:

someFunction(try throwingFunctionReturningAValue())

right?

Then you should be able to do the same with a property with a throwing getter:

someFunction(try throwingGetterProperty)

And then I guess that the same should be valid for an inout parameter. I don't
think that it makes the semantics less clear: No matter where the 'try' is
placed, you don't really know whether the throw will happen in the getter
(meaning that 'someFunction' and the setter will never be called) or in the
setter.

Just my 2 cents. Thanks again for a very thorough proposal draft!

Do you have an imagined use for throwing getters?

In the proposal, I cite a pair of framework methods that I think would be better modeled as a throwing subscript:

  class NSURL {
    func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
    func setResourceValue(_ value: AnyObject?, forKey key: String) throws
  }

Here's another, much less matter-of-opinion usage:

    // Numeric types, Bool, String, JSONValue?, [JSONValue], and [String: JSONValue] conform.
  protocol JSONValue { ... }
  
  protocol JSONRepresentable {
    associatedtype JSONRepresentation: JSONValue
    
    init(json: JSONRepresentation) throws
    var json: JSONRepresentation { get throws }
  }
  
  struct User: JSONRepresentable {
    var json: [String: JSONValue]
    
    var friends: [User] {
      get throws {
        guard let friendsJSON = json["friends"] as? [User.JSONRepresentation] else {
          throw UserError.InvalidFriends
        }
        return friendsJSON.map { try User(json: $0) }
      }
      set throws {
        json["friends"] = newValue.map { try $0.json }
      }
    }
  }

As long as we're doing computation in getters, it will make sense for that computation to raise errors. I don't think we can get around the need for `get throws`.

Allowing the getter or setter to throw independently greatly complicates the abstract model for properties. While we don't have much in the way of abstraction tools over properties yet, it would be important to consider the impact this might have on "lens" functions that can perform mutable projections. Right now, you can more or less categorize mutable properties into two groups:

- "reference-like", projecting mutable storage indirectly through a reference, which you could think of as having a type (Base) -> inout Property. This includes not only class properties but `nonmutating` struct properties like `UnsafeMutablePointer.memory`.
- "value-like", projecting mutable storage that is part of a larger mutable value, which you could think of as having type (inout Base) -> inout Property. This includes most mutable struct properties that aren't explicitly `nonmutating`.

I think you may be overoptimistic here—I've tried to prototype lenses before (using protocols and classes) and I've always quickly ended up in combinatorial explosion territory even when merely modeling existing behavior. First of all, mutability is uglier than you imply:

- There's actually a third setter category: read-only.
- The getter and setter can be *independently* mutating—Swift is happy to accept `mutating get nonmutating set` (although I can't imagine why you would need it).

So that's already six, not two, categories.

Another complication comes from the type of the property in the lens's view. You need Any-typed lenses for KVC-style metaprogramming, but you also want type-specialized lenses for greater safety where you have stronger type guarantees. And yet their setters are different: Any setters need to be able to signal that they couldn't downcast to the concrete type of the property you were mutating. (This problem can actually go away if you have throwing setters, though—an Any lens just has to make nonthrowing setters into throwing ones!)

(For added fun: you can't model the relationship between an Any lens and a specialized lens purely in protocols, because that would require support for higher-kinded types.)

So if you want to model the full richness of property semantics through their lenses, the lens system will inevitably be complicated. If you're willing to give up some fidelity when you convert to lenses, well, you can give up fidelity on throwing semantics too, and have the lens throw if either accessor throws.

If properties can throw, we'd want to be able to represent that in the type system for projection functions. If there are four different ways a property can throw (no throws, only reads can throw, only writes can throw, or both reads and writes can throw), that gets really complicated.

Nevertheless, this might be a good enough reason to invoke the escape hatch I left in the Alternatives Considered section:

  Calling a setter often implicitly involves calling a getter, so it may
  make sense to require the setter to be at least as throwing as the
  getter. Absent feedback to this effect from implementors, however, my
  instinct is to leave them independent…

The way I see it, we have three options for the throwing variants we might allow:

- Independent: Any combination
- Dependent: Getter can't throw unless setter can (no "get throws set")
- Setter only: Getter can't throw (no "get throws set" or "get throws set throws")

We also might want to look at that weird nonmutating case, so there are two options there:

- Independent: Any combination
- Dependent: Getter can't mutate unless setter can (no "nonmutating get mutating set")

(If we really wanted to, we could probably make the `mutating` property setter-only; things like `lazy` would just have to use a reference-typed box. That would be a pretty violent change to the language, though.)

That's a lot of possible semantics, so I wrote a script to help me understand what each policy would do. Here's the abridged results:

  20 variants for independent mutating, independent throws
  16 variants for independent mutating, dependent throws
  16 variants for dependent mutating, independent throws
  13 variants for dependent mutating, dependent throws
  12 variants for independent mutating, setter-only throws
  10 variants for dependent mutating, setter-only throws

(Full results, plus script: <https://gist.github.com/brentdax/97e3dfe7af208da51a1a>. By the way, without any `throws` support at all, independent mutating requires 6 variants and dependent mutating requires 5.)

My main conclusion is that limiting `throws` to setters only doesn't actually gain us much over limiting both `throws` and `mutating` to the "sensible" combinations.

And again, this only applies if we want lenses to model all property semantics with complete fidelity. We could offer fewer lenses if we're willing to accept clunkier interfaces. Heck, we could offer a single lens type if we're okay with dynamic safety:

  // Semantics would be equivalent to:
  struct Lens<Instance, Value> {
    // Can throw only errors thrown by the property.
    func value(for instance: inout Instance) throws -> Value

    // Can throw `SetterInaccessible` or errors thrown by the property.
    func updateValue<T>(for instance: inout Instance, mutator: (inout Value) -> T) throws -> T
    
    // Can throw `RequiresMutating` or errors thrown by the property.
    func value(for instance: Instance) throws -> Value
    
    // Can throw `SetterInaccessible`, `RequiresMutating`, or errors thrown by the property.
    func updateValue<T>(for instance: Instance, mutator: (inout Value) -> T) throws -> T
  }
  extension Lens {
    // `updateValue` methods can also throw `InvalidValueType`.
    init<ConcreteValue: Value>(downcasting: Lens<Instance, ConcreteValue>)
  }
  enum LensError: Error {
    case InvalidValueType (expectedType: Any.Type, foundType: Any.Type)
    case SetterInaccessible
    case RequiresMutating
  }

···

--
Brent Royal-Gordon
Architechies

I wonder if it would help to document the grammar changes necessary in the proposal.

I'm preparing a revision with that information.

I also wonder how it could affect the getter shorthand? Couldn't the following work for subscripts?

   subscript(idx: Int) throws -> Element { ... }

   // the above is shorthand for
   subscript(idx: Int) -> Element {
       get throws { ... }
   }

A shorthand is a definite possibility, especially for subscripts where both accessors might need to throw because of an invalid index, but I decided to keep it simple. I'm also worried that it might encourage people to mark accessors as throwing when only one actually needs it.

Not sure how this could be extended to work with general computed vars. This doesn't feel right:

   var x throws: Int { ... }

Yeah, that's another problem.

···

--
Brent Royal-Gordon
Architechies

Do you have an imagined use for throwing getters?

The project I work on — a database engine <https://github.com/couchbase/couchbase-lite-ios> — has an API with a lot of things that are conceptually properties, but whose accessors can fail because they may have to read or write persistent storage. Some examples are Database.documentCount and Document.currentRevision.

In the current (Objective-C) API, some of these are exposed as methods that use the standard Cocoa NSError-returning pattern, while some that are very unlikely to fail are exposed as actual properties; the implementations either return a distinct failure value like nil, or just a default value like 0. I’m not saying this is great, but it’s a tradeoff between correctness and ease of use where we’ve sometimes leaned toward ease of use.

Failable property accessors would be a big help here, letting us have an expressive API where properties are properties, while not losing the ability to do proper error handling.

The same applies to subscripts, btw. Since our data stores are key-value collections it’s convenient to be able to subscript a database by key to get a value. Of course this has the potential to fail due to file corruption, disk I/O errors, etc.

As another example, I’ve done some experimental work in the past on incremental JSON parsing; if using such a library, errors are not necessarily discovered up front, so it’s possible for accessing an item in a JSON array or dictionary to fail with a parse error. (Yes, you could syntax-check ahead of time, but since the major purpose of the incremental parsing is speed, that would be self-defeating.)

—Jens

Do you have an imagined use for throwing getters?

In the proposal, I cite a pair of framework methods that I think would be better modeled as a throwing subscript:

  class NSURL {
    func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
    func setResourceValue(_ value: AnyObject?, forKey key: String) throws
  }

Here's another, much less matter-of-opinion usage:

   // Numeric types, Bool, String, JSONValue?, [JSONValue], and [String: JSONValue] conform.
  protocol JSONValue { ... }
  
  protocol JSONRepresentable {
    associatedtype JSONRepresentation: JSONValue
    
    init(json: JSONRepresentation) throws
    var json: JSONRepresentation { get throws }
  }
  
  struct User: JSONRepresentable {
    var json: [String: JSONValue]
    
    var friends: [User] {
      get throws {
        guard let friendsJSON = json["friends"] as? [User.JSONRepresentation] else {
          throw UserError.InvalidFriends
        }
        return friendsJSON.map { try User(json: $0) }
      }
      set throws {
        json["friends"] = newValue.map { try $0.json }
      }
    }
  }

As long as we're doing computation in getters, it will make sense for that computation to raise errors. I don't think we can get around the need for `get throws`.

It's debatable whether this is a good use of property syntax. The standard library doesn't even use property syntax for things that might unconditionally fail due to programmer error.

Allowing the getter or setter to throw independently greatly complicates the abstract model for properties. While we don't have much in the way of abstraction tools over properties yet, it would be important to consider the impact this might have on "lens" functions that can perform mutable projections. Right now, you can more or less categorize mutable properties into two groups:

- "reference-like", projecting mutable storage indirectly through a reference, which you could think of as having a type (Base) -> inout Property. This includes not only class properties but `nonmutating` struct properties like `UnsafeMutablePointer.memory`.
- "value-like", projecting mutable storage that is part of a larger mutable value, which you could think of as having type (inout Base) -> inout Property. This includes most mutable struct properties that aren't explicitly `nonmutating`.

I think you may be overoptimistic here—I've tried to prototype lenses before (using protocols and classes) and I've always quickly ended up in combinatorial explosion territory even when merely modeling existing behavior. First of all, mutability is uglier than you imply:

- There's actually a third setter category: read-only.

How is that different from a nonmutating setter? Did you mean a read-only property? A read-only property is just a regular function, Base -> Property.

- The getter and setter can be *independently* mutating—Swift is happy to accept `mutating get nonmutating set` (although I can't imagine why you would need it).

Fair point. From the point of view of the property abstraction, though, `mutating get nonmutating set` is erased to `mutating get mutating set`. That leaves three kinds of mutable property projection. `mutating get` itself is sufficiently weird and limited in utility, its use cases (IMO) better handled by value types holding onto a class instance for their lazy- or cache-like storage, that it might be worth jettisoning as well.

Another complication comes from the type of the property in the lens's view. You need Any-typed lenses for KVC-style metaprogramming, but you also want type-specialized lenses for greater safety where you have stronger type guarantees. And yet their setters are different: Any setters need to be able to signal that they couldn't downcast to the concrete type of the property you were mutating. (This problem can actually go away if you have throwing setters, though—an Any lens just has to make nonthrowing setters into throwing ones!)

This sounds like something generics would better model than Any polymorphism, to carry the type parameter through the context you need polymorphism.

(For added fun: you can't model the relationship between an Any lens and a specialized lens purely in protocols, because that would require support for higher-kinded types.)

So if you want to model the full richness of property semantics through their lenses, the lens system will inevitably be complicated. If you're willing to give up some fidelity when you convert to lenses, well, you can give up fidelity on throwing semantics too, and have the lens throw if either accessor throws.

True, you could say that if either part of the access can throw, then the entire property access is abstractly considered `throws`, and that errors are checked after get, after set, and for an `inout` access, when materializeForSet is called before the formal inout access (to catch get errors), and also after the completion callback is invoked (to catch set errors). That means you have to `try` every access to an abstracted property, but that's not the end of the world.

-Joe

···

On Mar 14, 2016, at 4:59 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

Joe, was there an off-list email? I don't see your comments in my mail client or on Mailman, only quoted by Brent.

···

On Mar 14, 2016, at 7:59 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Do you have an imagined use for throwing getters?

In the proposal, I cite a pair of framework methods that I think would be better modeled as a throwing subscript:

   class NSURL {
       func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws
       func setResourceValue(_ value: AnyObject?, forKey key: String) throws
   }

Here's another, much less matter-of-opinion usage:

     // Numeric types, Bool, String, JSONValue?, [JSONValue], and [String: JSONValue] conform.
   protocol JSONValue { ... }
   
   protocol JSONRepresentable {
       associatedtype JSONRepresentation: JSONValue
       
       init(json: JSONRepresentation) throws
       var json: JSONRepresentation { get throws }
   }
   
   struct User: JSONRepresentable {
       var json: [String: JSONValue]
       
       var friends: [User] {
           get throws {
               guard let friendsJSON = json["friends"] as? [User.JSONRepresentation] else {
                   throw UserError.InvalidFriends
               }
               return friendsJSON.map { try User(json: $0) }
           }
           set throws {
               json["friends"] = newValue.map { try $0.json }
           }
       }
   }

As long as we're doing computation in getters, it will make sense for that computation to raise errors. I don't think we can get around the need for `get throws`.

Allowing the getter or setter to throw independently greatly complicates the abstract model for properties. While we don't have much in the way of abstraction tools over properties yet, it would be important to consider the impact this might have on "lens" functions that can perform mutable projections. Right now, you can more or less categorize mutable properties into two groups:

- "reference-like", projecting mutable storage indirectly through a reference, which you could think of as having a type (Base) -> inout Property. This includes not only class properties but `nonmutating` struct properties like `UnsafeMutablePointer.memory`.
- "value-like", projecting mutable storage that is part of a larger mutable value, which you could think of as having type (inout Base) -> inout Property. This includes most mutable struct properties that aren't explicitly `nonmutating`.

I think you may be overoptimistic here—I've tried to prototype lenses before (using protocols and classes) and I've always quickly ended up in combinatorial explosion territory even when merely modeling existing behavior. First of all, mutability is uglier than you imply:

- There's actually a third setter category: read-only.
- The getter and setter can be *independently* mutating—Swift is happy to accept `mutating get nonmutating set` (although I can't imagine why you would need it).

So that's already six, not two, categories.

Another complication comes from the type of the property in the lens's view. You need Any-typed lenses for KVC-style metaprogramming, but you also want type-specialized lenses for greater safety where you have stronger type guarantees. And yet their setters are different: Any setters need to be able to signal that they couldn't downcast to the concrete type of the property you were mutating. (This problem can actually go away if you have throwing setters, though—an Any lens just has to make nonthrowing setters into throwing ones!)

(For added fun: you can't model the relationship between an Any lens and a specialized lens purely in protocols, because that would require support for higher-kinded types.)

So if you want to model the full richness of property semantics through their lenses, the lens system will inevitably be complicated. If you're willing to give up some fidelity when you convert to lenses, well, you can give up fidelity on throwing semantics too, and have the lens throw if either accessor throws.

If properties can throw, we'd want to be able to represent that in the type system for projection functions. If there are four different ways a property can throw (no throws, only reads can throw, only writes can throw, or both reads and writes can throw), that gets really complicated.

Nevertheless, this might be a good enough reason to invoke the escape hatch I left in the Alternatives Considered section:

   Calling a setter often implicitly involves calling a getter, so it may
   make sense to require the setter to be at least as throwing as the
   getter. Absent feedback to this effect from implementors, however, my
   instinct is to leave them independent…

The way I see it, we have three options for the throwing variants we might allow:

- Independent: Any combination
- Dependent: Getter can't throw unless setter can (no "get throws set")
- Setter only: Getter can't throw (no "get throws set" or "get throws set throws")

We also might want to look at that weird nonmutating case, so there are two options there:

- Independent: Any combination
- Dependent: Getter can't mutate unless setter can (no "nonmutating get mutating set")

(If we really wanted to, we could probably make the `mutating` property setter-only; things like `lazy` would just have to use a reference-typed box. That would be a pretty violent change to the language, though.)

That's a lot of possible semantics, so I wrote a script to help me understand what each policy would do. Here's the abridged results:

   20 variants for independent mutating, independent throws
   16 variants for independent mutating, dependent throws
   16 variants for dependent mutating, independent throws
   13 variants for dependent mutating, dependent throws
   12 variants for independent mutating, setter-only throws
   10 variants for dependent mutating, setter-only throws

(Full results, plus script: <https://gist.github.com/brentdax/97e3dfe7af208da51a1a>. By the way, without any `throws` support at all, independent mutating requires 6 variants and dependent mutating requires 5.)

My main conclusion is that limiting `throws` to setters only doesn't actually gain us much over limiting both `throws` and `mutating` to the "sensible" combinations.

And again, this only applies if we want lenses to model all property semantics with complete fidelity. We could offer fewer lenses if we're willing to accept clunkier interfaces. Heck, we could offer a single lens type if we're okay with dynamic safety:

   // Semantics would be equivalent to:
   struct Lens<Instance, Value> {
       // Can throw only errors thrown by the property.
       func value(for instance: inout Instance) throws -> Value

       // Can throw `SetterInaccessible` or errors thrown by the property.
       func updateValue<T>(for instance: inout Instance, mutator: (inout Value) -> T) throws -> T
       
       // Can throw `RequiresMutating` or errors thrown by the property.
       func value(for instance: Instance) throws -> Value
       
       // Can throw `SetterInaccessible`, `RequiresMutating`, or errors thrown by the property.
       func updateValue<T>(for instance: Instance, mutator: (inout Value) -> T) throws -> T
   }
   extension Lens {
       // `updateValue` methods can also throw `InvalidValueType`.
       init<ConcreteValue: Value>(downcasting: Lens<Instance, ConcreteValue>)
   }
   enum LensError: Error {
       case InvalidValueType (expectedType: Any.Type, foundType: Any.Type)
       case SetterInaccessible
       case RequiresMutating
   }

--
Brent Royal-Gordon
Architechies

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

Not to my knowledge.

-Joe

···

On Mar 14, 2016, at 6:08 PM, Step C <schristopher@bignerdranch.com> wrote:

Joe, was there an off-list email? I don't see your comments in my mail client or on Mailman, only quoted by Brent.

As long as we're doing computation in getters, it will make sense for that computation to raise errors. I don't think we can get around the need for `get throws`.

It's debatable whether this is a good use of property syntax. The standard library doesn't even use property syntax for things that might unconditionally fail due to programmer error.

Debatable, sure.

My intuition is that, if you have a pair of things which act as getter and setter, Swift ought to permit you to treat them as a single member. If Swift erects a barrier preventing you from doing that, that is a limitation of the language and ought to be considered a negative. That doesn't man we are *obliged* to add features to support every weird setter variation—for instance, I doubt we're ever going to accommodate UIKit's love of `setFoo(_:animated:)` methods—but I think we ought to lean towards making properties and subscripts as powerful as methods when we can reasonably do so.

- There's actually a third setter category: read-only.

How is that different from a nonmutating setter? Did you mean a read-only property? A read-only property is just a regular function, Base -> Property.

Yes, I mean a read-only property (no setter). Currently these aren't exposed at all on the type, even though we could provide read-only access.

So I take it what you're proposing is that this:

  struct Foo {
    func method() { ... }
    mutating func mutatingMethod() { ... }
    
    var readOnlyProperty: Int { get { ... } }
    var readOnlyMutatingProperty: Int { mutating get { ... } }
    
    var readWriteProperty: Int { get { ... } set { ... } }
    var readWriteNonmutatingProperty: Int { get { ... } nonmutating set { ... } }
  }

Also has these members?

  extension Foo {
    // These argument lists might be combined in the future
    static func method(self: Foo) -> () -> Void
    static func mutatingMethod(self: inout Foo) -> () -> Void
    
    static func readOnlyProperty(self: Foo) -> Int
    static func readOnlyMutatingProperty(self: inout Foo) -> Int
    
    static func readWriteProperty(self: inout Foo) -> inout Int
    static func readWriteNonmutatingProperty(self: Foo) -> inout Int
  }

(Hmm. There might be room for a `reinout` or `inout(set)` along the lines of `rethrows`:

  static func readWriteProperty(self: reinout Foo) -> inout Int

That would mean the `self` parameter is inout only if the return value is treated as inout.)

- The getter and setter can be *independently* mutating—Swift is happy to accept `mutating get nonmutating set` (although I can't imagine why you would need it).

Fair point. From the point of view of the property abstraction, though, `mutating get nonmutating set` is erased to `mutating get mutating set`. That leaves three kinds of mutable property projection.

That can work. But like I said, you could do the same thing with `throws`.

`mutating get` itself is sufficiently weird and limited in utility, its use cases (IMO) better handled by value types holding onto a class instance for their lazy- or cache-like storage, that it might be worth jettisoning as well.

That would interfere with the rather elegant mutating-get-to-copy-on-write pattern: <https://developer.apple.com/videos/play/wwdc2015/414/?time=2044>. I suppose a mutating method would work the same way as long as you were backing the instance with a reference type, though.

Another complication comes from the type of the property in the lens's view. You need Any-typed lenses for KVC-style metaprogramming, but you also want type-specialized lenses for greater safety where you have stronger type guarantees. And yet their setters are different: Any setters need to be able to signal that they couldn't downcast to the concrete type of the property you were mutating. (This problem can actually go away if you have throwing setters, though—an Any lens just has to make nonthrowing setters into throwing ones!)

This sounds like something generics would better model than Any polymorphism, to carry the type parameter through the context you need polymorphism.

Sorry, that probably wasn't as clear as it should be.

I would like to eventually have a way to dynamically look up and use lenses by property name. I think this could serve as a replacement for KVC. So, for instance, the `Foo` type I showed previously might have dictionaries on it equivalent to these:

  extension Foo {
    static var readableProperties: [String: (inout Foo) -> inout Any] = [
      "readWriteProperty": { $0.readWriteProperty },
      "readWriteNonmutatingProperty": { ... },
      "readOnlyProperty": { ... },
      "readOnlyMutatingProperty": { ... }
    ]
    static var writableProperties: [String: (inout Foo) -> inout Any] = [
      "readWriteProperty": {
        get { return $0.readWriteProperty }
        set { $0.readWriteProperty = newValue as! Int }
      },
      "readWriteNonmutatingProperty": { ... }
    ]
  }

Then you could dynamically look up and use a lens:

  for (propertyName, value) in configurationDictionary {
    let property = Foo.writableProperties[propertyName]!
    property(&foo) = value
  }

(These dictionaries would not be opted into with a protocol; they would be synthesized at a given use site, and would contain all properties visible at that site. That would allow you to pass a dictionary of lenses to external serialization code, essentially delegating your access to those properties.)

But note the forced cast in the `readWriteProperty` setter: if the Any assigned into it turns out to be of the wrong type, the program will crash. That's not good. So the setter has to be able to signal its failure, and the only way I can imagine to do that would be to have it throw an exception:

    static var writableProperties: [String: (inout Foo) throws -> inout Any] = [
      "readWriteProperty": {
        get { return $0.readWriteProperty }
        set {
          guard let newValue = newValue as? Int else {
            throw PropertyError.InvalidType
          }
          $0.readWriteProperty = newValue
        }
      },
      …
  
  for (propertyName, value) in configurationDictionary {
    let property = Foo.writableProperties[propertyName]!
    try property(&foo) = value
  }

Hence, non-throwing setters would have to become throwing setters when you erased the property's concrete type.

(For added fun: you can't model the relationship between an Any lens and a specialized lens purely in protocols, because that would require support for higher-kinded types.)

So if you want to model the full richness of property semantics through their lenses, the lens system will inevitably be complicated. If you're willing to give up some fidelity when you convert to lenses, well, you can give up fidelity on throwing semantics too, and have the lens throw if either accessor throws.

True, you could say that if either part of the access can throw, then the entire property access is abstractly considered `throws`, and that errors are checked after get, after set, and for an `inout` access, when materializeForSet is called before the formal inout access (to catch get errors), and also after the completion callback is invoked (to catch set errors). That means you have to `try` every access to an abstracted property, but that's not the end of the world.

Exactly.

(Although in theory, we could add a `throws(set)` syntax on inout-returning functions, indicating that the getter never throws but the setter does, much like the `reinout` I mentioned earlier.)

···

--
Brent Royal-Gordon
Architechies

A mutable return has to be invariant. It'd be more sound to model that as "exists T. (inout Foo) -> inout T", if our type system becomes powerful enough to represent that, than to erase the return type, since you could then conceivably still recover the T by opening the existential.

-Joe

···

On Mar 15, 2016, at 5:01 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

  extension Foo {
    static var readableProperties: [String: (inout Foo) -> inout Any] = [
      "readWriteProperty": { $0.readWriteProperty },
      "readWriteNonmutatingProperty": { ... },
      "readOnlyProperty": { ... },
      "readOnlyMutatingProperty": { ... }
    ]
    static var writableProperties: [String: (inout Foo) -> inout Any] = [
      "readWriteProperty": {
        get { return $0.readWriteProperty }
        set { $0.readWriteProperty = newValue as! Int }
      },
      "readWriteNonmutatingProperty": { ... }
    ]
  }

A mutable return has to be invariant. It'd be more sound to model that as "exists T. (inout Foo) -> inout T", if our type system becomes powerful enough to represent that, than to erase the return type, since you could then conceivably still recover the T by opening the existential.

I'm trying to unpack this statement. I *think* what you're saying is that, since a given lens always returns the same type, the lens should itself be stored in an existential, rather than returning an existential, so you can discover the type it returns. In other words, instead of being "function from mutable Foo to mutable any", like this:

  inout Foo throws -> inout Any

They should be "any function from mutable Foo to some mutable type", which would be something along the lines of:

  Any<T in inout Foo throws -> inout T>

(Although that syntax is a catastrophe—best I could come up with, but still horrible. `T in` declares a generic type parameter, if it's not clear.)

Is that accurate?

If so, it still seems like Swift ought to let you set from Any using the existential so you don't have to explicitly unwrap it all the time. That would provide something equivalent to this:

  extension<T> Any<inout Foo throws -> inout T> {
    // Inside the extension, T is bound to the return type, so we can open `self` by casting it with T.
    
    invoke(instance: inout Foo) throws -> inout Any {
      get {
        let concreteInvoke = self as inout Foo throws -> inout T
        return try concreteInvoke(&instance) as Any
      }
      set {
        let concreteInvoke = self as inout Foo throws -> inout T
        guard let newValue = newValue as? T {
          throw InvalidTypeError
        }
        try concreteInvoke(&instance) = newValue
      }
    }
  }

And it also seems like, if we decide not to take the generics system far enough to support this kind of existential, an implementation using type `inout Foo -> throws inout Any` is a good compromise. (Though we shouldn't rush to offer it if we do intend to support that kind of existential later on.)

In any case, however, this is digressing pretty far from the proposal itself.

* * *

If I recall correctly, Joe, your objections were only to throwing getters, and were as follows:

— Abstracting over the property might become more complicated if there are many possible combinations of throwabiity.

Is it enough to say that we can make a lens throw if either accessor can throw, as we've been discussing?

Do you think it doesn't make sense to support `get throws set` configurations? (Given that `get` and `set` are often combined into a single operation, I can understand why you would feel that way.) If so, do you think we should also consider (in a separate proposal) eliminating `mutating get nonmutating set` configurations?

— You think that code which can throw should generally be a method, not a getter.

I've already mentioned my opinion on the appropriateness of this:

My intuition is that, if you have a pair of things which act as getter and setter, Swift ought to permit you to treat them as a single member. If Swift erects a barrier preventing you from doing that, that is a limitation of the language and ought to be considered a negative.

I also have a couple of practical concerns.

I worry that, if Swift doesn't support throwing from getters, people will not respond by using methods; they will instead respond by handling errors in suboptimal ways, such as:

* Using `try!` or other trapping error handling mechanisms inside the getter, making errors impossible to handle gracefully.
* Returning optionals, which will throw away error information and permit the setter to accept meaningless `nil` values.
* Returning implicitly unwrapped optionals, which have the same disadvantages as optionals, in addition to undermining Swift's goal of forcing you to explicitly handle expected errors.
* Returning `Result`-style enums, which will make the property more difficult to use and permit the setter to accept meaningless error values.
* Abusing a future universal error mechanism, which would undermine Swift's goal of forcing you to explicitly handle anticipatable errors.
* Abusing exception mechanisms from other languages bridged into Swift, which would cause undefined behavior.

I also think that, in places where a protocol requirement might reasonably be fulfilled by either non-throwing storage or potentially-throwing computation, the absence of throwing getters might force you to favor one implementation or the other. For instance, consider the JSONRepresentable protocol I used in an earlier example:

  protocol JSONRepresentable {
    associatedtype JSONRepresentation: JSONValue
    
    init(json: JSONRepresentation) throws
    var json: JSONRepresentation { get throws }
  }

Some types are best implemented by storing the data in one property as (unpacked) JSON; others are best implemented by storing the data in separate properties and generating the JSON on demand. If getters can't throw, then `json` must either be a non-throwing getter (disadvantaging types which generate the JSON) or a throwing method (disadvantaging types which store the JSON).

Finally, I think that even if properties might not have strong enough use cases, subscripts will. For instance:

  enum XMLNode {
    // ...cases omitted...
    
    private var underlyingNode: NSXMLNode {
      // Omitted because massive switch statements.
      get { ... }
      set { ... }
    }
    private var mutatingUnderlyingNode: NSXMLNode {
      mutating get {
        underlyingDocument = underlyingDocument.copy() as! NSXMLDocument
        return underlyingDocument
      }
    }
    
    subscript(xPath path: String) -> [XMLNode] {
      get throws {
        return try underlyingNode.nodesForXPath(path).map(XMLNode.init)
      }
      set throws {
        let oldUnderlyingValue = try mutatingUnderlyingNode.nodesForXPath(path)

        precondition(newValue.count == oldUnderlyingValue.count)
        for (newNode, oldUnderlyingNode) in zip(newValue, oldUnderlyingValue) {
          newNode.supplant(oldUnderlyingNode)
        }
      }
    }
  }

And if you think that methods should replace throwing property getters, then surely once inout-returning functions are introduced, their getters will need to throw. At that point you'll be in a situation where most getters in Swift can throw, but there's a specific exception for property getters, arising out of a linguistic opinion rather than a general rule. That's the sort of strange inconsistency Swift is otherwise good at avoiding. (For instance, I was very pleased indeed to discover the `while let` loop last year.)

Even if you and like-minded programmers don't like them, I can't see any practical reason to forbid throwing getters. They don't appear to undermine any of the error handling system's goals, introduce confusing or inconsistent behavior, or severely complicate the compiler's job. They merely stretch the role of a property somewhat. And it's a pretty mild kind of stretching; I think throwing getters are less strange, for instance, than the generic constants Chris Lattner and others are interested in introducing.

I do understand why you and other developers might not want to use throwing getters, but I think that's ultimately a matter of style, not of correctness. Throwing getters seem like a design choice that reasonable people might make.

Even if you would not use throwing property getters, do you agree that they're conceptually well-formed and others with differing opinions might want to use them?

···

--
Brent Royal-Gordon
Architechies

Is there any news on whether or not this will ever be implemented?

Throwing properties and subscripts would we a really neat addition.

This would also allow adding dynamic throwing properties when using it along dynamic member lookup from SE-0195 which would be nice for Python interop.

3 Likes

I would also like to see this. It might also apply to both get and set if they are both supplied:

subscript(idx: Int) throws -> Element {
    get {...} //Get can throw
    set {...} //Set can throw
}

How about, if you want throws for computed properties, you just have to spell it the long way:

var x:Int {
    get throws { ... }
} 

The only other reasonable alternative IMO is:

var x:Int throws

Somehow I missed this thread when it originally happened.

One piece of concrete feedback:

I’m not sure what you’re imagining by “optimizations” here, but it’s not actually reasonable to try to prevent errors from being thrown between the start and end of an access. For example, the function taking the inout parameter might itself just be a throwing function. This creates a semantic problem if we’re required to call the setter because we might end up with multiple errors in flight at once.

I think the best solution is to revise the semantic model for all accesses to distinguish between aborting and completing an access, with the idea that aborting a mutation to storage defined with set / willSet / didSet causes the accessor call to be skipped.

2 Likes