Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Forked from elm4ward/KeyPathBinding.swift
Last active February 3, 2018 23:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriseidhof/f46856afbba9cbdcf1dd3681c194cce8 to your computer and use it in GitHub Desktop.
Save chriseidhof/f46856afbba9cbdcf1dd3681c194cce8 to your computer and use it in GitHub Desktop.
KeyPath
// ------------------------------------
// 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
@elm4ward
Copy link

elm4ward commented Sep 8, 2017

Line 95 is a lil typo:
binding.right(group, response) instead of binding.right(group, r)

@chriseidhof
Copy link
Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment