Last active
September 14, 2020 07:12
-
-
Save mbrandonw/4acd26ab01bb6140af69 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*: | |
Brandon Williams | |
# Lenses in Swift | |
*/ | |
/*: | |
## i.e. Functional getters and setters | |
*/ | |
/*: | |
# Prisms in Swift | |
*/ | |
/*: | |
## The dual of Lens | |
*/ | |
/*: | |
#### i.e. ??? | |
*/ | |
/*: | |
# Boundaries | |
*/ | |
/*: | |
### How to do getters and setters in a fully immutable world | |
*/ | |
struct Location { | |
let name: String | |
} | |
struct User { | |
let name: String | |
let location: Location | |
} | |
struct Project { | |
let name: String | |
let creator: User | |
let location: Location | |
} | |
extension Location : CustomDebugStringConvertible { | |
var debugDescription: String { | |
return "{\(name)}" | |
} | |
} | |
extension User : CustomDebugStringConvertible { | |
var debugDescription: String { | |
return "{\(name), \(location)}" | |
} | |
} | |
extension Project : CustomDebugStringConvertible { | |
var debugDescription: String { | |
return "{\(name), \(creator), \(location)}" | |
} | |
} | |
let la = Location(name: "Los Angeles") | |
let joel = User(name: "Joel", location: la) | |
let mn = Location(name: "Minneapolis") | |
let mst3k = Project( | |
name: "Bring Back MYSTERY SCIENCE THEATER 3000", | |
creator: joel, | |
location: mn) | |
la.name | |
//la.name = "LA" | |
extension User { | |
func getName() -> String { | |
return self.name | |
} | |
func setName(name: String) -> User { | |
return User(name: name, location: self.location) | |
} | |
} | |
joel.getName() | |
joel.setName("Joel Hodgson") | |
joel.getName() | |
extension Project { | |
func getCreator() -> User { | |
return self.creator | |
} | |
func setCreator(creator: User) -> Project { | |
return Project(name: self.name, creator: creator, location: self.location) | |
} | |
} | |
mst3k.setCreator(joel.setName("Joel Hodgson")) | |
mst3k.setCreator(mst3k.getCreator().setName("Joel Hodgson")) | |
/* | |
func get (A) -> B | |
func get (Whole) -> Part | |
func set (Part, Whole) -> Whole | |
*/ | |
struct Lens <A, B> { | |
let get: A -> B | |
let set: (B, A) -> A | |
} | |
let nameLens = Lens<User, String>( | |
get: { user in user.name }, | |
set: { (name, user) in User(name: name, location: user.location) } | |
) | |
nameLens.get(joel) | |
nameLens.set("Joel Hodgson", joel) | |
let creatorLens = Lens<Project, User>( | |
get: { project in project.creator }, | |
set: { creator, project in Project(name: project.name, creator: creator, location: project.location) } | |
) | |
creatorLens.set( | |
nameLens.set( | |
"Joel Hodgson", | |
creatorLens.get(mst3k) | |
), | |
mst3k | |
) | |
creatorLens | |
nameLens | |
func compose <A, B, C> (lhs: Lens<A, B>, _ rhs: Lens<B, C>) -> Lens<A, C> { | |
return Lens<A, C>( | |
get: { a in rhs.get(lhs.get(a)) }, | |
set: { (c, a) in lhs.set(rhs.set(c, lhs.get(a)), a) } | |
) | |
} | |
let creatorNameLens = compose(creatorLens, nameLens) | |
creatorNameLens.get(mst3k) | |
func * <A, B, C> (lhs: Lens<A, B>, rhs: Lens<B, C>) -> Lens<A, C> { | |
return compose(lhs, rhs) | |
} | |
(creatorLens * nameLens).get(mst3k) | |
(creatorLens * nameLens).set("Joel Hodgson", mst3k) | |
extension Location { | |
static let nameLens = Lens<Location, String>( | |
get: { $0.name }, | |
set: { (name, _) in Location(name: name) } | |
) | |
} | |
extension User { | |
static let nameLens = Lens<User, String>( | |
get: { $0.name }, | |
set: { (n, u) in User(name: n, location: u.location) } | |
) | |
static let locationLens = Lens<User, Location>( | |
get: { $0.location }, | |
set: { (l, u) in User(name: u.name, location: l) } | |
) | |
} | |
extension Project { | |
static let nameLens = Lens<Project, String>( | |
get: { $0.name }, | |
set: { (n, p) in Project(name: n, creator: p.creator, location: p.location) } | |
) | |
static let creatorLens = Lens<Project, User>( | |
get: { $0.creator }, | |
set: { (c, p) in Project(name: p.name, creator: c, location: p.location) } | |
) | |
static let locationLens = Lens<Project, Location>( | |
get: { $0.location }, | |
set: { (l, p) in Project(name: p.name, creator: p.creator, location: l) } | |
) | |
} | |
(Project.creatorLens * User.locationLens * Location.nameLens).set("LA", mst3k) | |
//{Bring Back MYSTERY SCIENCE THEATER 3000, {Joel, {LA}}, {Minneapolis}} | |
infix operator *~ { associativity left precedence 100 } | |
func *~ <A, B> (lhs: Lens<A, B>, rhs: B) -> A -> A { | |
return { a in lhs.set(rhs, a) } | |
} | |
(User.nameLens *~ "Joel Hodgson")(joel) | |
infix operator |> {associativity left precedence 80 } | |
func |> <A, B> (x: A, f: A -> B) -> B { | |
return f(x) | |
} | |
func |> <A, B, C> (f: A -> B, g: B -> C) -> A -> C { | |
return { g(f($0)) } | |
} | |
joel |> User.nameLens *~ "Joel Hodgson" | |
//mst3k |> | |
Project.locationLens * Location.nameLens *~ "LA" | |
|> Project.creatorLens * User.nameLens *~ "Joel Hodgson" | |
|> Project.creatorLens * User.locationLens * Location.nameLens *~ "New York" | |
//{Bring Back MYSTERY SCIENCE THEATER 3000, {Joel Hodgson, {New York}}, {LA}} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment