Created
February 26, 2016 01:27
-
-
Save safx/f2884872c43fde9c4c93 to your computer and use it in GitHub Desktop.
Lensy separates LensType into (non-failable) LensType and FailableLensType
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
// | |
// Lensy.swift | |
// Lensy | |
// | |
// Created by Safx Developer on 2016/02/12. | |
// Copyright © 2016 Safx Developers. All rights reserved. | |
// | |
// MARK: - Lenses protocols | |
protocol LensType { | |
typealias Whole | |
typealias Part | |
var get: Whole -> Part { get } | |
var set: (Whole, Part) -> Whole { get } | |
} | |
protocol FailableLensType { | |
typealias Whole | |
typealias Part | |
var get: Whole -> LensResult<Part> { get } | |
var set: (Whole, Part) -> LensResult<Whole> { get } | |
} | |
// MARK: - Lenses structs | |
struct Lens<Whole, Part>: LensType { | |
let get: Whole -> Part | |
let set: (Whole, Part) -> Whole | |
} | |
struct FailableLens<Whole, Part>: FailableLensType { | |
let get: Whole -> LensResult<Part> | |
let set: (Whole, Part) -> LensResult<Whole> | |
} | |
struct OptionalUnwrapLens<Element>: FailableLensType { | |
typealias Whole = Element? | |
typealias Part = Element | |
let get: Whole -> LensResult<Part> | |
let set: (Whole, Part) -> LensResult<Whole> | |
} | |
struct ArrayIndexLens<Element>: FailableLensType { | |
typealias Whole = [Element] | |
typealias Part = Element | |
let get: Whole -> LensResult<Part> | |
let set: (Whole, Part) -> LensResult<Whole> | |
} | |
// MARK: - Lenses extensions | |
extension LensType { | |
func compose<Subpart, L: LensType where Self.Part == L.Whole, L.Part == Subpart>(other: L) -> Lens<Whole, Subpart> { | |
return Lens<Whole, Subpart>( | |
get: { other.get(self.get($0)) }, | |
set: { object, newValue in | |
return self.set(object, other.set(self.get(object), newValue)) | |
} | |
) | |
} | |
func compose<Subpart, L: FailableLensType where Self.Part == L.Whole, L.Part == Subpart>(other: L) -> FailableLens<Whole, Subpart> { | |
return FailableLens<Whole, Subpart>( | |
get: { other.get(self.get($0)) }, | |
set: { object, newValue in | |
return other.set(self.get(object), newValue) | |
.then { .OK(self.set(object, $0)) } | |
} | |
) | |
} | |
func modify(object: Whole, _ closure: Part -> Part) -> Whole { | |
return self.set(object, closure(get(object))) | |
} | |
} | |
extension FailableLensType { | |
func compose<Subpart, L: FailableLensType where Self.Part == L.Whole, L.Part == Subpart>(other: L) -> FailableLens<Whole, Subpart> { | |
return FailableLens<Whole, Subpart>( | |
get: { (object: Whole) -> LensResult<Subpart> in | |
return self.get(object) | |
.then(other.get) | |
}, | |
set: { (object: Whole, newValue: Subpart) -> LensResult<Whole> in | |
return self.get(object) | |
.then { other.set($0, newValue) } | |
.then { self.set(object, $0) } | |
} | |
) | |
} | |
func compose<Subpart, L: LensType where Self.Part == L.Whole, L.Part == Subpart>(other: L) -> FailableLens<Whole, Subpart> { | |
return FailableLens<Whole, Subpart>( | |
get: { (object: Whole) -> LensResult<Subpart> in | |
return self.get(object) | |
.then { .OK(other.get($0)) } | |
}, | |
set: { (object: Whole, newValue: Subpart) -> LensResult<Whole> in | |
return self.get(object) | |
.then { .OK(other.set($0, newValue)) } | |
.then { self.set(object, $0) } | |
} | |
) | |
} | |
func modify(object: Whole, _ closure: Part -> Part) -> LensResult<Whole> { | |
return get(object) | |
.then { self.set(object, closure($0)) } | |
} | |
func tryGet(object: Whole) throws -> Part { | |
switch get(object) { | |
case .OK(let v): | |
return v | |
case .Error(let e): | |
throw e | |
} | |
} | |
func trySet(object: Whole, _ newValue: Part) throws -> Whole { | |
switch set(object, newValue) { | |
case .OK(let v): | |
return v | |
case .Error(let e): | |
throw e | |
} | |
} | |
} | |
extension OptionalUnwrapLens { | |
init() { | |
get = { optional in | |
guard let v = optional else { | |
return .Error(LensErrorType.OptionalNone) | |
} | |
return .OK(v) | |
} | |
set = { optional, newValue in | |
guard var v = optional else { | |
return .Error(LensErrorType.OptionalNone) | |
} | |
v = newValue | |
return .OK(v) | |
} | |
} | |
} | |
extension ArrayIndexLens { | |
init(at idx: Int) { | |
get = { array in | |
guard 0..<array.count ~= idx else { | |
return .Error(LensErrorType.ArrayIndexOutOfBounds) | |
} | |
return .OK(array[idx]) | |
} | |
set = { array, newValue in | |
guard 0..<array.count ~= idx else { | |
return .Error(LensErrorType.ArrayIndexOutOfBounds) | |
} | |
var arr = array | |
arr[idx] = newValue | |
return .OK(arr) | |
} | |
} | |
} | |
// MARK: - Lens Utility Function | |
private func createIdentityLens<Whole>() -> Lens<Whole, Whole> { | |
return Lens<Whole, Whole>( | |
get: { $0 }, | |
set: { $1 } | |
) | |
} | |
// MARK: - Result | |
enum LensResult<Element> { | |
case OK(Element) | |
case Error(LensErrorType) | |
} | |
extension LensResult { | |
func then<T>(closure: Element -> LensResult<T>) -> LensResult<T> { | |
switch self { | |
case .OK(let v): | |
return closure(v) | |
case .Error(let e): | |
return .Error(e) | |
} | |
} | |
} | |
// MARK: - Error | |
enum LensErrorType: ErrorType { | |
case OptionalNone | |
case ArrayIndexOutOfBounds | |
} | |
// MARK: - Lens Helper | |
protocol LensHelperType { | |
typealias Whole | |
typealias Part | |
init(lens: Lens<Whole, Part>?, failableLens: FailableLens<Whole, Part>?) | |
var lens: Lens<Whole, Part>? { get } | |
var failableLens: FailableLens<Whole, Part>? { get } | |
} | |
protocol HasSubLensHelper { | |
typealias SubLensHelper | |
} | |
struct LensHelper<Whole, Part>: LensHelperType { | |
let lens: Lens<Whole, Part>? | |
let failableLens: FailableLens<Whole, Part>? | |
} | |
struct ArrayLensHelper<Whole, Element, Sub>: LensHelperType, HasSubLensHelper { | |
typealias Part = [Element] | |
typealias SubLensHelper = Sub | |
let lens: Lens<Whole, Part>? | |
let failableLens: FailableLens<Whole, Part>? | |
} | |
struct OptionalLensHelper<Whole, Element, Sub>: LensHelperType, HasSubLensHelper { | |
typealias Part = Element? | |
typealias SubLensHelper = Sub | |
let lens: Lens<Whole, Part>? | |
let failableLens: FailableLens<Whole, Part>? | |
} | |
extension LensHelperType { | |
init<Parent: LensHelperType where Parent.Whole == Whole>(parent: Parent, lens: Lens<Parent.Part, Part>) { | |
if let l = parent.lens { | |
self.init(lens: l.compose(lens), failableLens: nil) | |
return | |
} else if let l = parent.failableLens { | |
self.init(lens: nil, failableLens: l.compose(lens)) | |
return | |
} | |
fatalError() | |
} | |
func unsafeGet(object: Whole) -> Part { | |
if let l = lens { | |
return l.get(object) | |
} else if let l = failableLens { | |
if case let .OK(v) = l.get(object) { | |
return v | |
} | |
} | |
fatalError() | |
} | |
func unsafeSet(object: Whole, _ newValue: Part) -> Whole { | |
if let l = lens { | |
return l.set(object, newValue) | |
} else if let l = failableLens { | |
if case let .OK(v) = l.set(object, newValue) { | |
return v | |
} | |
} | |
fatalError() | |
} | |
func get(object: Whole) -> LensResult<Part> { | |
if let l = lens { | |
return .OK(l.get(object)) | |
} else if let l = failableLens { | |
return l.get(object) | |
} | |
fatalError() | |
} | |
func set(object: Whole, _ newValue: Part) -> LensResult<Whole> { | |
if let l = lens { | |
return .OK(l.set(object, newValue)) | |
} else if let l = failableLens { | |
return l.set(object, newValue) | |
} | |
fatalError() | |
} | |
} | |
extension ArrayLensHelper where Sub: LensHelperType, Sub.Whole == Whole, Sub.Part == Element { | |
subscript(idx: Int) -> SubLensHelper { | |
if let l = lens { | |
return SubLensHelper(lens: nil, failableLens: l.compose(ArrayIndexLens<Element>(at: idx))) | |
} else if let l = failableLens { | |
return SubLensHelper(lens: nil, failableLens: l.compose(ArrayIndexLens<Element>(at: idx))) | |
} | |
fatalError() | |
} | |
} | |
extension OptionalLensHelper where Sub: LensHelperType, Sub.Whole == Whole, Sub.Part == Element { | |
var unwrap: SubLensHelper { | |
if let l = lens { | |
return SubLensHelper(lens: nil, failableLens: l.compose(OptionalUnwrapLens<Element>())) | |
} else if let l = failableLens { | |
return SubLensHelper(lens: nil, failableLens: l.compose(OptionalUnwrapLens<Element>())) | |
} | |
fatalError() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment