> On Mar 14, 2016, at 4:59 PM, Brent Royal-Gordon <[email protected]> 
> 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`.

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
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to