Skip to content

Instantly share code, notes, and snippets.

@elm4ward
Last active November 15, 2018 20:39
Show Gist options
  • Save elm4ward/910bac9ba5ab896d9d631e3389c57f1e to your computer and use it in GitHub Desktop.
Save elm4ward/910bac9ba5ab896d9d631e3389c57f1e to your computer and use it in GitHub Desktop.
Searching for a Traced (Cowriter) usecase ...
// --------------------------------------------------------------------------------
// 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))
@elm4ward
Copy link
Author

elm4ward commented Nov 8, 2017

Problem is that acl in the Updateis kind of badly attached (e.g. gets dropped in <>).

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