Last active
July 24, 2018 08:21
-
-
Save elm4ward/8ae29ca20d87c4b3e53aaa7f5c74ca36 to your computer and use it in GitHub Desktop.
This file contains 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
import Foundation | |
// --------------------- | |
// Actions | |
// --------------------- | |
typealias Closure<S> = (inout S) -> Void | |
/// Our Actions are basically a Tree of Nodes | |
/// Data for multiple args | |
indirect enum GenericAction<S> { | |
case children(String, [GenericAction<S>]) | |
case codable(String, Data?) | |
case closure(Closure<S>) | |
case hook(String, GenericAction<S>, Middleware<S>) | |
} | |
typealias Middleware<S> = (inout S, (GenericAction<S>, inout S) -> Void) -> Void | |
/// All Actions defined here. | |
/// The protocol can be extended and so on. | |
protocol ActionAlgebra { | |
associatedtype StateType | |
static func middleware(_: String, _: Self, _ : @escaping Middleware<StateType>) -> Self | |
static func state(_: @escaping ((inout StateType) -> Void)) -> Self | |
static func url(_: String) -> Self | |
static func and(_ a: Self, _ b: Self) -> Self | |
} | |
// --------------------- | |
// State | |
// --------------------- | |
/// This is a basic state, whatever | |
struct State { | |
var stringVal: String | |
var intValue: Int = 0 | |
} | |
/// Helper init | |
extension State { | |
init(){ | |
stringVal = "" | |
} | |
} | |
/// State Actions | |
protocol Actions: ActionAlgebra where StateType == State {} | |
// --------------------- | |
// String of Action | |
// --------------------- | |
/// Just a to String | |
extension String: Actions { | |
typealias StateType = State | |
static func and(_ a: String, _ b: String) -> String { | |
return "\(a) and \(b)" | |
} | |
static func state(_ c: @escaping ((inout State) -> Void)) -> String { | |
return "Change \(c)" | |
} | |
static func url(_ url: String) -> String { | |
return "Get Url '\(url)'" | |
} | |
static func middleware(_ name: String, _ then: String, _ mw: @escaping Middleware<State>) -> String { | |
return "\(name) > \(then)" | |
} | |
} | |
// --------------------- | |
// Node of Action | |
// --------------------- | |
let d = JSONDecoder() | |
let e = JSONEncoder() | |
typealias Action = GenericAction<State> | |
/// Returns a Node for an Action | |
extension GenericAction: ActionAlgebra where S == State { | |
typealias StateType = State | |
static func & (_ lhs: Action, _ rhs: Action) -> Action { | |
return and(lhs, rhs) | |
} | |
static func and(_ a: Action, _ b: Action) -> Action { | |
return .children("and", [a, b]) | |
} | |
static func url(_ u: String) -> Action { | |
return .codable("url", try? e.encode([u])) | |
} | |
static func state(_ c: @escaping ((inout State) -> Void)) -> Action { | |
return .closure(c) | |
} | |
static func middleware(_ n: String, _ t: GenericAction<State>, _ mw: @escaping Middleware<State>) -> Action { | |
return .hook(n, t, mw) | |
} | |
} | |
// --------------------- | |
// Interpreter of Action Effect | |
// --------------------- | |
struct ActionInterpreter { | |
let run: (Action, inout State) -> Void | |
} | |
extension ActionInterpreter: Actions { | |
typealias StateType = State | |
static func and(_ a1: ActionInterpreter, _ a2: ActionInterpreter) -> ActionInterpreter { | |
return ActionInterpreter { a, s in | |
a1.run(a, &s) | |
a2.run(a, &s) | |
} | |
} | |
static func state(_ c: @escaping ((inout State) -> Void)) -> ActionInterpreter { | |
return ActionInterpreter { a, s in | |
c(&s) | |
} | |
} | |
static func middleware(_ n: String, _ a1: ActionInterpreter, _ mw: @escaping Middleware<State>) -> ActionInterpreter { | |
return ActionInterpreter { action, state in | |
mw(&state, a1.run) | |
} | |
} | |
static func url(_ u : String) -> ActionInterpreter { | |
return ActionInterpreter { _, _ in | |
/// TODO | |
} | |
} | |
} | |
// --------------------- | |
// Reduce and interpret | |
// --------------------- | |
/// Data Helper | |
func from<D: Decodable>(_ data: Data, at: Int) -> D { | |
return try! d.decode([D].self, from: data)[at] | |
} | |
/// Algebra Parser | |
func parse<E: Actions>(_ node: GenericAction<E.StateType>) -> E { | |
switch node { | |
case let .codable("url", data?): | |
return .url(from(data, at: 0)) | |
case let .children("and", l): | |
return .and(parse(l[0]), parse(l[1])) | |
case let .hook(name, then, mw): | |
return .middleware(name, parse(then), mw) | |
case let .closure(c): | |
return .state(c) | |
default: | |
print("node failed:", node) | |
fatalError() | |
} | |
} | |
/// Reducer | |
func reduce(_ action: Action, _ state: inout State){ | |
let s: ActionInterpreter = parse(action) | |
s.run(action, &state) | |
} | |
/// State and Actions | |
var state = State() | |
var increment: Action = .state { | |
$0.intValue = $0.intValue + 1 | |
} | |
var test1: Action = .state { | |
$0.stringVal = "test1" | |
} | |
var test2: Action = .state { | |
$0.stringVal = "test2" | |
} | |
var network: Action = .url("url/") | |
func middleware(hook: @escaping (Action, inout State, (Action, inout State) -> Void) -> Void) -> (Action) -> Action { | |
return { action in | |
.middleware("log", action){ s, f in | |
hook(action, &s, f) | |
} | |
} | |
} | |
var logging = middleware { action, state, next in | |
let desc: String = parse(action) | |
print("will log on action:", desc) | |
print(" - before:", state) | |
next(action, &state) | |
print(" - after: ", state) | |
} | |
var highlight = middleware { action, state, next in | |
let desc: String = parse(action) | |
print(" ####", desc) | |
print(" - before:", state) | |
next(action, &state) | |
print(" - after: ", state) | |
print(" ####") | |
} | |
/// Reduce on with state | |
reduce(network, &state) | |
reduce(test1, &state) | |
reduce(logging(test2 & highlight(increment)), &state) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment