Skip to content

Instantly share code, notes, and snippets.

@ukitaka
Last active December 18, 2016 06:15
Show Gist options
  • Select an option

  • Save ukitaka/319dda356621b71e43ccca25229925e3 to your computer and use it in GitHub Desktop.

Select an option

Save ukitaka/319dda356621b71e43ccca25229925e3 to your computer and use it in GitHub Desktop.
Lens
struct Lens<S, A> {
typealias Getter = (S) -> A
typealias Setter = (S, A) -> S
fileprivate let _get: Getter
fileprivate let _set: Setter
init(get: @escaping Getter, set: @escaping Setter) {
self._get = get
self._set = set
}
func get(_ s: S) -> A {
return _get(s)
}
func set(_ s: S) -> (A) -> S {
return curry(_set)(s)
}
func modify(_ s: S) -> ((A) -> A) -> S {
return { f in
self.set(s)(f(self.get(s)))
}
}
}
// MARK: -
precedencegroup MonadicPrecedenceRight {
associativity: right
higherThan: AssignmentPrecedence
}
infix operator >=>: MonadicPrecedenceRight
func >=> <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: { (a, c) -> A in
lhs.set(a)(rhs.set(lhs.get(a))(c))
}
)
}
// MARK: -
func curry<A, B>(_ function: @escaping (A) -> B) -> (A) -> B {
return { (a: A) -> B in function(a) }
}
func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C {
return { (a: A) -> (B) -> C in { (b: B) -> C in function(a, b) } }
}
struct Employee {
let name: String
let company: Company
}
struct Company {
let name: String
let address: Address
}
struct Address {
let city: String
let street: Street
}
struct Street {
let number: Int
let name: String
}
// MARK: -
let employeeCompanyLens = Lens<Employee, Company>(
get: { employee in
employee.company
},
set: { employee, company in
Employee(name: employee.name, company: company)
}
)
let companyAddressLens = Lens<Company, Address>(
get: { company in
company.address
},
set: { company, address in
Company(name: company.name, address: address)
}
)
let addressStreetLens = Lens<Address, Street>(
get: { address in
address.street
},
set: { address, street in
Address(city: address.city, street: street)
}
)
let streetNumberLens = Lens<Street, Int>(
get: { street in
street.number
},
set: { street, number in
Street(number: number, name: street.name)
}
)
// MARK: -
let employeeCompanyAddressStreetNumberLens
= employeeCompanyLens >=> companyAddressLens >=> addressStreetLens >=> streetNumberLens
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment