Last active
November 15, 2018 20:39
-
-
Save elm4ward/910bac9ba5ab896d9d631e3389c57f1e to your computer and use it in GitHub Desktop.
Searching for a Traced (Cowriter) usecase ...
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
// -------------------------------------------------------------------------------- | |
// MARK: - Operators | |
// -------------------------------------------------------------------------------- | |
precedencegroup Cobind { associativity: left } | |
precedencegroup Cokleisli { | |
associativity: left | |
higherThan: Cobind | |
} | |
precedencegroup Concat { | |
associativity: left | |
higherThan: Cokleisli | |
} | |
precedencegroup PropSet { | |
associativity: left | |
higherThan: Concat | |
lowerThan: AdditionPrecedence | |
} | |
infix operator ^ : PropSet | |
infix operator ->- :Cokleisli | |
infix operator ->> :Cobind | |
infix operator <> :Concat | |
// -------------------------------------------------------------------------------- | |
// MARK: - Typealias and functions | |
// -------------------------------------------------------------------------------- | |
typealias Endo<T> = (T) -> T | |
let uppercased = String.uppercased | |
let inc: Endo<Int> = { $0 + 1 } | |
func id<A>(_ a: A) -> A { return a } | |
// -------------------------------------------------------------------------------- | |
// MARK: - Semigroup and Monoid | |
// -------------------------------------------------------------------------------- | |
protocol Semigroup { | |
static func <>(lhs: Self, rhs: Self) -> Self | |
} | |
protocol Monoid: Semigroup { | |
static var empty: Self { get } | |
} | |
extension Array: Monoid { | |
static var empty: Array { | |
return [] | |
} | |
static func <>(lhs: Array, rhs: Array) -> Array { | |
return lhs + rhs | |
} | |
} | |
// -------------------------------------------------------------------------------- | |
// MARK: - Acl | |
// -------------------------------------------------------------------------------- | |
enum Acl { | |
case any | |
case admin | |
} | |
// -------------------------------------------------------------------------------- | |
// MARK: - Update | |
// -------------------------------------------------------------------------------- | |
struct Update<T>: Monoid { | |
let acl: Acl | |
let update: Endo<T> | |
init(_ s: @escaping Endo<T>, _ a: Acl = .any){ | |
acl = a | |
update = s | |
} | |
init<V>(_ k: WritableKeyPath<T, V>, _ v: V, _ a: Acl = .any) { | |
acl = a | |
update = { t in | |
var tt = t | |
tt[keyPath: k] = v | |
return tt | |
} | |
} | |
init<V>(_ k: WritableKeyPath<T, V>, _ f: @escaping (V) -> V, _ a: Acl = .any) { | |
acl = a | |
update = { t in | |
var tt = t | |
tt[keyPath: k] = f(t[keyPath: k]) | |
return tt | |
} | |
} | |
init<V>(_ k: WritableKeyPath<T, V?>, _ f: @escaping (V) -> V, _ a: Acl = .any) { | |
acl = a | |
update = { t in | |
guard let v = t[keyPath: k] else { return t } | |
var tt = t | |
tt[keyPath: k] = f(v) | |
return tt | |
} | |
} | |
init<V>(_ k: WritableKeyPath<T, V?>, _ f: @escaping (V) -> () -> V, _ a: Acl = .any){ | |
acl = a | |
update = { t in | |
guard let v = t[keyPath: k] else { return t } | |
var tt = t | |
tt[keyPath: k] = f(v)() | |
return tt | |
} | |
} | |
static var empty: Update { | |
return Update({ $0 }) | |
} | |
static func <>(rhs: Update, lhs: Update) -> Update { | |
return Update({ d in lhs.update(rhs.update(d)) }) | |
} | |
} | |
// -------------------------------------------------------------------------------- | |
// MARK: - Update operators | |
// -------------------------------------------------------------------------------- | |
func | <T, V>(lhs: Acl, rhs: WritableKeyPath<T, V>) -> (Acl, WritableKeyPath<T, V>) { | |
return (lhs, rhs) | |
} | |
func ^ <T, V>(lhs: WritableKeyPath<T, V>, rhs: V) -> Update<T> { | |
return Update(lhs, rhs) | |
} | |
func ^ <T, V>(lhs: (Acl, WritableKeyPath<T, V>), rhs: V) -> Update<T> { | |
return Update(lhs.1, rhs, lhs.0) | |
} | |
func ^ <T, V>(lhs: WritableKeyPath<T, V?>, rhs: @escaping (V) -> V) -> Update<T> { | |
return Update(lhs, rhs) | |
} | |
func ^ <T, V>(lhs: WritableKeyPath<T, V?>, rhs: @escaping (V) -> () -> V) -> Update<T> { | |
return Update(lhs, rhs) | |
} | |
func ^ <T, V>(lhs: WritableKeyPath<T, V>, rhs: @escaping (V) -> V) -> Update<T> { | |
return Update(lhs, rhs) | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - Traced | |
// ---------------------------------------------------------------------------------------------------- | |
struct Traced<A, M: Monoid> { | |
let tell: (M) -> (A) | |
static func unit<A, M>(_ a: A) -> Traced<A, M> { | |
return Traced<A, M> { _ in a } | |
} | |
func extract() -> A { | |
return tell(M.empty) | |
} | |
func map<B>(_ f: @escaping (A) -> B) -> Traced<B,M> { | |
return Traced<B, M> { m in f(self.tell(m)) } | |
} | |
func duplicate() -> Traced<Traced<A,M>, M> { | |
return Traced<Traced<A,M>, M> { m in Traced { mx in self.tell(m <> mx) } } | |
} | |
func extend<B>(_ f: @escaping (Traced<A, M>) -> B) -> Traced<B, M> { | |
return duplicate().map(f) | |
} | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - Traced Operators | |
// ---------------------------------------------------------------------------------------------------- | |
func ->- <A, M, B, C>(lhs: @escaping (Traced<A, M>) -> B, rhs: @escaping (Traced<B, M>) -> C) -> ((Traced<A, M>) -> C) { | |
return { t in | |
t.extend(lhs).extend(rhs).extract() | |
} | |
} | |
func ->> <A, M, B>(lhs: Traced<A, M>, rhs: @escaping (Traced<A, M>) -> B) -> Traced<B, M> { | |
return lhs.extend(rhs) | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - Traced functions | |
// ---------------------------------------------------------------------------------------------------- | |
func tell<M: Monoid, A>(_ m: M) -> (Traced<A, M>) -> A { | |
return { $0.tell(m) } | |
} | |
func stack<A>(_ m: Update<A>) -> (Traced<[Update<A>], [Update<A>]>) -> [Update<A>] { | |
return { $0.tell([m]) } | |
} | |
// ---------------------------------------------------------------------------------------------------- | |
// MARK: - Example | |
// ---------------------------------------------------------------------------------------------------- | |
struct Person { | |
var name: String? | |
var age: Int | |
var mail: String? | |
} | |
let withAuth: Endo<[Update<Person>]> = { $0 } | |
let withoutAuth: Endo<[Update<Person>]> = { $0.filter { $0.acl == .any } } | |
let update: (Traced<[Update<Person>], [Update<Person>]>) -> [Update<Person>] = | |
stack(.admin | \.mail ^ "test") | |
->- stack(\.age ^ 37) | |
->- stack(\.name ^ String.uppercased) | |
let unauthorizedUpdates = Traced(tell: withoutAuth) ->> update | |
let authorizedUpdates = Traced(tell: withAuth) ->> update | |
let unauthUp = unauthorizedUpdates.extract().reduce(Update.empty, <>) | |
let authUp = authorizedUpdates.extract().reduce(Update.empty, <>) | |
let before = Person(name: "a", age: 30, mail: nil) | |
print("before", before) | |
print("unauthorized", unauthUp.update(before)) | |
print("authorized", authUp.update(before)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Problem is that
acl
in theUpdate
is kind of badly attached (e.g. gets dropped in<>
).