Skip to content

Instantly share code, notes, and snippets.

@mbrandonw
Last active September 14, 2020 07:12
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mbrandonw/4acd26ab01bb6140af69 to your computer and use it in GitHub Desktop.
Save mbrandonw/4acd26ab01bb6140af69 to your computer and use it in GitHub Desktop.
/*:
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