Skip to content

Instantly share code, notes, and snippets.

@safx
Created February 26, 2016 01:27
Show Gist options
  • Save safx/f2884872c43fde9c4c93 to your computer and use it in GitHub Desktop.
Save safx/f2884872c43fde9c4c93 to your computer and use it in GitHub Desktop.
Lensy separates LensType into (non-failable) LensType and FailableLensType
//
// 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