Skip to content

Instantly share code, notes, and snippets.

@mbrandonw
Last active March 18, 2018 14:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbrandonw/ec2dd6ec768702f5a6766aaf435c1e31 to your computer and use it in GitHub Desktop.
Save mbrandonw/ec2dd6ec768702f5a6766aaf435c1e31 to your computer and use it in GitHub Desktop.
Functional setters using Swift key paths
// ----------------------------------------------------
// These 3 helpers is all you need to unlock functional
// setters for every field of every immutable struct.
// ----------------------------------------------------
// A generic way to a lift writable key path and a value to a setter function.
func set<A, B>(_ kp: WritableKeyPath<A, B>, _ b: B) -> (A) -> A {
return { a in
var a = a
a[keyPath: kp] = b
return a
}
}
// A generic way to lift writable key paths and an update function to a setter function.
func over<A, B>(_ kp: WritableKeyPath<A, B>, _ update: @escaping (B) -> B) -> (A) -> A {
return { a in
var a = a
a[keyPath: kp] = update(a[keyPath: kp])
return a
}
}
// A generic way to apply many updates to a value.
func update<A>(_ a: A, _ update1: @escaping (A) -> A, _ updates: ((A) -> A)...) -> A {
var a = update1(a)
updates.forEach { a = $0(a) }
return a
}
// ----------------------------------------------------
// And here is how you can use them.
// ----------------------------------------------------
// Some types to play with.
struct Food { private(set) var name: String }
struct Location { private(set) var name: String }
struct User {
private(set) var favoriteFood: Food
private(set) var location: Location
private(set) var name: String
}
// A value to play with.
let user = User(
favoriteFood: Food(name: "Tacos"),
location: Location(name: "Brooklyn"),
name: "Blob"
)
// Create a new user with their location set to Los Angeles.
update(
user,
set(\.location.name, "Los Angeles")
)
// Create a new user by setting a bunch of properties at once.
update(
user,
set(\.location.name, "Los Angeles"),
set(\.name, "Blobby"),
over(\.favoriteFood.name) { "\($0) & Nachos" }
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment