Skip to content

Instantly share code, notes, and snippets.

@xNekOIx
Last active January 4, 2016 09:40
Show Gist options
  • Save xNekOIx/c664cbff1556b6c8b189 to your computer and use it in GitHub Desktop.
Save xNekOIx/c664cbff1556b6c8b189 to your computer and use it in GitHub Desktop.
import Foundation
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
enum Effects<A> {
typealias Action = A
case Nothing
case EffectTask(Task<Any, Action>)
case Batch([Effects<Action>])
}
extension Effects {
func append(effects: Effects<Action>) -> Effects<Action> {
switch (self, effects) {
case (.Nothing, _): return effects
case (_, .Nothing): return self
case (.EffectTask, .EffectTask): return .Batch([self, effects])
case (.EffectTask, .Batch(let effectsArray)): return .Batch([self] + effectsArray)
case (.Batch(let effectsArray), .EffectTask): return .Batch(effectsArray + [effects])
case (.Batch(let effs1), .Batch(let effs2)): return .Batch(effs1 + effs2)
default: fatalError("unhandled case")
}
}
func map<U>(f: Action -> U) -> Effects<U> {
switch self {
case .Nothing: return .Nothing
case .EffectTask(let task): return .EffectTask(task.map(f))
case .Batch(let effects): return .Batch(effects.map { $0.map(f) })
}
}
}
enum Result<T, ErrorType> {
typealias SuccessType = T
case Success(value: SuccessType), Failure(error: ErrorType)
func map<U>(f: T -> U) -> Result<U, ErrorType> {
switch self {
case .Success(let value): return .Success(value: f(value))
case .Failure(let error): return .Failure(error: error)
}
}
}
struct NoError: ErrorType {}
class Future<T> {
var value: T? {
willSet(newValue) {
assert(value == nil, "Trying to resolve future with value \(newValue) failed\nFuture already resolved with value \(value)")
assert(newValue != nil, "Try to evaluate Future with nil failed")
}
didSet {
guard let newVal = value else { return }
subscribers.forEach { $0(newVal) }
subscribers = []
}
}
private var subscribers: [T -> ()] = []
func subscribe(subscriber: T -> ()) {
switch value {
case .Some: subscriber(value!)
case .None: subscribers.append(subscriber)
}
}
func map<U>(f: T -> U) -> Future<U> {
let nextF = Future<U>()
subscribe({ nextF.value = f($0) })
return nextF
}
}
struct Task<T, S> {
let execute: T -> Future<S>
}
extension Task {
func task<T, S>(work: T -> S, result: S -> () = { _ in }) -> Task<T, S> {
var future = Future<S>()
future.subscribe(result)
let task = Task<T, S>(execute: { v in
future.value = work(v)
return future
})
return task
}
}
extension Task {
func map<U>(f: S -> U) -> Task<T, U> {
return Task<T, U>(execute: { v in
return self.execute(v).map(f)
})
}
}
struct URLService {
let session = NSURLSession.sharedSession()
}
func HTTPTask(request: NSURLRequest) -> Task<URLService, NSData> {
let task = Task<URLService, NSData> { (service) -> Future<NSData> in
let future = Future<NSData>()
let session = service.session
session.dataTaskWithRequest(request, completionHandler: { data, response, error in
if let data = data {
future.value = data
return
}
}).resume()
return future
}
return task
}
public class Store<State, Action> {
private var state: State
private var update: (State, Action) -> (State, Effects<Action>)
private var services: [String: Any] = [:]
init(initialState: State, effects: Effects<Action> = .Nothing, update: (State, Action) -> (State, Effects<Action>)) {
dump(initialState)
dump(effects)
print("\n===================\n")
state = initialState
self.update = update
dispatchEffects(effects)
}
private let queue = dispatch_queue_create("com.Store", DISPATCH_QUEUE_SERIAL)
public func dispatch(action: Action) {
dispatch_async(queue) {
let (state, effects) = self.update(self.state, action)
self.state = state
dump(action)
dump(self.state)
dump(effects)
print("\n===================\n")
self.dispatchEffects(effects)
}
}
private func dispatchEffects(effects: Effects<Action>) {
dispatch_async(queue) {
switch effects {
case .Nothing: break
case .EffectTask(let task):
switch task {
case Task<(), Action>: task.map(self.dispatch).execute()
default: task.map(self.dispatch).execute(self.service())
}
case .Batch(let effects):
effects.forEach(self.dispatchEffects)
}
}
}
func registerService<T>(service: T) {
let key = "\(T.self)"
services[key] = service
}
private func service<T>() -> T {
let key = "\(T.self)"
return services[key] as! T
}
}
struct Counter {
typealias State = UInt
enum Action {
case Increment, TrueIncrement, RequestIncrement
}
static func update(state: State, action: Action) -> (State, Effects<Action>) {
var state = state
var effects: Effects<Action> = .Nothing
switch action {
case .RequestIncrement:
let task = HTTPTask(request: NSURLRequest(URL: NSURL(string: "http://pewpew.com")!))
.map { _ in return Action.TrueIncrement }
effects = effects.append(.EffectTask(task))
case .Increment:
effects = effects.append(.EffectTask(task(work: { return Action.TrueIncrement })))
case .TrueIncrement:
state = state + 1
}
return (state, effects)
}
}
struct TwoCounters {
struct State {
var left: Counter.State
var right: Counter.State
}
enum Action {
case UpdateLeft(act: Counter.Action), UpdateRight(act: Counter.Action)
}
static func update(state: State, action: Action) -> (State, Effects<Action>) {
var state = state
var effects: Effects<Action> = .Nothing
switch action {
case .UpdateLeft(let act):
let (newLeft, newEffects) = Counter.update(state.left, action: act)
state.left = newLeft
effects = newEffects.map { Action.UpdateLeft(act: $0) }
case .UpdateRight(let act):
let (newRight, newEffects) = Counter.update(state.right, action: act)
state.right = newRight
effects = newEffects.map { Action.UpdateRight(act: $0) }
}
return (state, effects)
}
}
let store: Store<TwoCounters.State, TwoCounters.Action> = Store(initialState: .init(left: 0, right: 0), update: TwoCounters.update)
store.registerService(URLService())
store.dispatch(.UpdateLeft(act: .Increment))
store.dispatch(.UpdateLeft(act: .RequestIncrement))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment