-
-
Save chriseidhof/f46856afbba9cbdcf1dd3681c194cce8 to your computer and use it in GitHub Desktop.
KeyPath
This file contains hidden or 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
// ------------------------------------ | |
// Network | |
// ------------------------------------ | |
struct MeetupResponse { | |
var groupName: String = "" | |
var groupOrganizers: [String] = [] | |
var meetupTitle: String = "" | |
var meetupAttendes: Int = 0 | |
var meetupSpeakers: [String]? = nil | |
} | |
// ------------------------------------ | |
// Domain | |
// ------------------------------------ | |
struct MeetupGroup { | |
var name: String = "" | |
var organizers: [String] = [] | |
var event: MeetupEvent = MeetupEvent() | |
} | |
struct MeetupEvent { | |
var title: String = "" | |
var attendes: Int = 0 | |
var speakers: [String]? = nil | |
} | |
// ------------------------------------ | |
// Binding | |
// ------------------------------------ | |
struct Binding<L, R> { | |
var left: (inout L, R) -> () | |
var right: (inout R, L) -> () | |
} | |
extension Binding { | |
init<V>(_ left: WritableKeyPath<L, V>, _ right: WritableKeyPath<R, V>){ | |
self.left = { l, r in | |
l[keyPath: left] = r[keyPath: right] | |
} | |
self.right = { r, l in | |
r[keyPath: right] = l[keyPath: left] | |
} | |
} | |
} | |
// ------------------------------------ | |
// Binding operator | |
// ------------------------------------ | |
precedencegroup BindGroup {} | |
infix operator ~ : BindGroup | |
func ~ <L, R, V>(lhs: WritableKeyPath<L, V>, rhs: WritableKeyPath<R, V>) -> Binding<L, R> { | |
return Binding(lhs, rhs) | |
} | |
// ------------------------------------ | |
// Binding | |
// ------------------------------------ | |
let bindings = [ | |
\MeetupResponse.groupOrganizers ~ \MeetupGroup.organizers, | |
\.groupName ~ \.name, | |
\.meetupTitle ~ \.event.title, | |
\.meetupAttendes ~ \.event.attendes, | |
\.meetupSpeakers ~ \.event.speakers | |
] | |
// ------------------------------------ | |
// Map from Response to Domain and back | |
// ------------------------------------ | |
var r = MeetupResponse() | |
r.groupName = "group" | |
r.groupOrganizers = ["a", "b"] | |
r.meetupTitle = "Meetup title" | |
r.meetupAttendes = 12 | |
r.meetupSpeakers = ["1", "2"] | |
let meetup = bindings.reduce(into: MeetupGroup()) { group, binding in binding.right(&group, r) } | |
let meetupResponse = bindings.reduce(into: MeetupResponse()) { group, binding in binding.left(&group, meetup) } | |
print(meetupResponse) | |
struct Lens<A,B> { | |
let get: (A) -> B | |
let set: (inout A, B) -> () | |
} | |
protocol Empty { init() } | |
extension MeetupGroup: Empty { } | |
func lens<A, B: Empty>(bindings: [Binding<A,B>]) -> Lens<A,B> { | |
return Lens<A, B>(get: { a in | |
return bindings.reduce(into: B(), { b, binding in | |
binding.right(&b, a) | |
}) | |
}, set: { a, b in | |
a = bindings.reduce(into: a) { group, binding in | |
binding.left(&group, b) | |
} | |
}) | |
} | |
let meetupLens = lens(bindings: bindings) | |
func binding<A, B>(bindings: [Binding<A,B>]) -> Binding<A,B> { | |
return Binding(left: { a, b in | |
a = bindings.reduce(into: a, { a, binding in | |
binding.left(&a, b) | |
}) | |
}, right: { b, a in | |
b = bindings.reduce(into: b) { b, binding in | |
binding.right(&b, a) | |
} | |
}) | |
} | |
var m = meetupLens.get(r) | |
m.name = "Test" | |
meetupLens.set(&r, m) | |
r.groupName |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 95 is a lil typo:
binding.right(group, response)
instead ofbinding.right(group, r)