Focus
Focus copied to clipboard
Optional traversal
Is there no way to lens through an optional? I've tried with Party.lpartyCaterer() • User.userName
and Party.lpartyCaterer() • _Some • User.userName
, to no avail.
// A party has a host, an optional caterer and an array of guests
class Party {
var host : User
var caterer : User?
var guests : [User]
init(h : User, c : User? = nil, g : [User] = []) {
host = h
caterer = c
guests = g
}
class func lpartyHost() -> Lens<Party, Party, User, User> {
let getter = { (party : Party) -> User in party.host }
let setter = { (party : Party, host : User) -> Party in Party(h: host, c: party.caterer, g: party.guests) }
return Lens(get: getter, set: setter)
}
class func lpartyCaterer() -> Lens<Party, Party, User?, User?> {
let getter = { (party : Party) -> User? in party.caterer }
let setter = { (party : Party, caterer : User?) -> Party in Party(h: party.host, c: caterer, g: party.guests) }
return Lens(get: getter, set: setter)
}
class func lpartyGuests() -> Lens<Party, Party, [User], [User]> {
let getter = { (party : Party) -> [User] in party.guests }
let setter = { (party : Party, guests : [User]) -> Party in Party(h: party.host, c: party.caterer, g: guests) }
return Lens(get: getter, set: setter)
}
}
class PartySpec : XCTestCase {
func testLens() {
let party = Party(h: User("max", 1, [], "one"))
let hostnameLens = Party.lpartyHost() • User.userName
XCTAssert(hostnameLens.get(party) == "max")
let updatedParty: Party = (Party.lpartyHost() • User.userName).set(party, "Max")
XCTAssert(hostnameLens.get(updatedParty) == "Max")
let catererLens = Party.lpartyCaterer() • User.userName
let cateredParty: Party = (Party.lpartyCaterer() • User.userName).set(party, "Marc")
XCTAssert(catererLens.get(updatedParty) == "Marc")
}
}
A Lens
is not a Prism
(in fact, they are sort of categorically dual), of which _Some()
must be because T? -> T
is, by its very nature, a partial operation. You need a prism hierarchy for this, not a lens hierarchy.
Or you could (though I definitely don't recommend this) define the type of a forcing lens for Optionals. Maybe call it _UnsafeSome
, then use that as your intermediary. Otherwise, because Swift doesn't let us express the variance of types, or treat this as a valid coercion, you have to rewrite the hierarchy.
I'd expect a Lens
composed with a Prism
to be a Focus.Optional
, but it looks like it isn't implemented: #4 😞
Is Optional unimplemented due to Swift language shortcomings, or just because no one has gotten around to it yet?
Little of one, little of the other. A lot of the reason we haven't implemented things is because it's just not worth it yet. A lot of the power of the Lens library is things generalize in a thousand little ways where in Swift you're lucky to get one.
Do you foresee any of the planned improvements in Swift 3 making the implementation more worthwhile?
Nope. Needs HKTs, which also necessitates a huge number of proposals that would fundamentally alter the language.