Skip to content

Commit

Permalink
Merge pull request #9 from pthariensflame/topic/protocols
Browse files Browse the repository at this point in the history
Optics protocols
  • Loading branch information
CodaFi committed Jul 14, 2015
2 parents 5e839ca + 44da645 commit 2125780
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 80 deletions.
34 changes: 20 additions & 14 deletions Focus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
11D137051B544D0000E76EBA /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D137041B544D0000E76EBA /* Protocols.swift */; };
11D137061B544D0000E76EBA /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11D137041B544D0000E76EBA /* Protocols.swift */; };
824B45F91B460AC1009AAF49 /* Focus.h in Headers */ = {isa = PBXBuildFile; fileRef = 824B45F81B460AC1009AAF49 /* Focus.h */; settings = {ATTRIBUTES = (Public, ); }; };
824B46001B460AC1009AAF49 /* Focus.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 824B45F51B460AC1009AAF49 /* Focus.framework */; };
824B461E1B460AFA009AAF49 /* Focus.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 824B46141B460AFA009AAF49 /* Focus.framework */; };
Expand Down Expand Up @@ -118,29 +120,30 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
11D137041B544D0000E76EBA /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; usesTabs = 1; };
824B45F51B460AC1009AAF49 /* Focus.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Focus.framework; sourceTree = BUILT_PRODUCTS_DIR; };
824B45F81B460AC1009AAF49 /* Focus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Focus.h; path = Focus/Focus.h; sourceTree = "<group>"; };
824B45FA1B460AC1009AAF49 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
824B45FF1B460AC1009AAF49 /* FocusTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FocusTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
824B46061B460AC1009AAF49 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
824B46141B460AFA009AAF49 /* Focus.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Focus.framework; sourceTree = BUILT_PRODUCTS_DIR; };
824B461D1B460AFA009AAF49 /* Focus-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Focus-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
824B462B1B460B25009AAF49 /* Iso.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Iso.swift; sourceTree = "<group>"; };
824B462C1B460B25009AAF49 /* IxCont.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxCont.swift; sourceTree = "<group>"; };
824B462D1B460B25009AAF49 /* IxMultiStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxMultiStore.swift; sourceTree = "<group>"; };
824B462E1B460B25009AAF49 /* IxState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxState.swift; sourceTree = "<group>"; };
824B462F1B460B25009AAF49 /* IxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxStore.swift; sourceTree = "<group>"; };
824B462B1B460B25009AAF49 /* Iso.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Iso.swift; sourceTree = "<group>"; usesTabs = 1; };
824B462C1B460B25009AAF49 /* IxCont.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxCont.swift; sourceTree = "<group>"; usesTabs = 1; };
824B462D1B460B25009AAF49 /* IxMultiStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxMultiStore.swift; sourceTree = "<group>"; usesTabs = 1; };
824B462E1B460B25009AAF49 /* IxState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxState.swift; sourceTree = "<group>"; usesTabs = 1; };
824B462F1B460B25009AAF49 /* IxStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IxStore.swift; sourceTree = "<group>"; usesTabs = 1; };
824B46301B460B25009AAF49 /* Lens.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lens.swift; sourceTree = "<group>"; usesTabs = 1; };
824B46311B460B25009AAF49 /* Prism.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Prism.swift; sourceTree = "<group>"; };
824B46411B460BE2009AAF49 /* ArrayZipper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayZipper.swift; sourceTree = "<group>"; };
824B46471B460C1E009AAF49 /* LensOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LensOperators.swift; sourceTree = "<group>"; };
824B464A1B460E69009AAF49 /* PartyExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartyExample.swift; sourceTree = "<group>"; };
824B464B1B460E69009AAF49 /* UserExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserExample.swift; sourceTree = "<group>"; };
824B46311B460B25009AAF49 /* Prism.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Prism.swift; sourceTree = "<group>"; usesTabs = 1; };
824B46411B460BE2009AAF49 /* ArrayZipper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayZipper.swift; sourceTree = "<group>"; usesTabs = 1; };
824B46471B460C1E009AAF49 /* LensOperators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LensOperators.swift; sourceTree = "<group>"; usesTabs = 1; };
824B464A1B460E69009AAF49 /* PartyExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartyExample.swift; sourceTree = "<group>"; usesTabs = 1; };
824B464B1B460E69009AAF49 /* UserExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserExample.swift; sourceTree = "<group>"; usesTabs = 1; };
82DD8EC71B48D1CA00B66551 /* SwiftCheck.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwiftCheck.xcodeproj; path = Carthage/Checkouts/SwiftCheck/SwiftCheck.xcodeproj; sourceTree = SOURCE_ROOT; };
82DD8EDE1B48D2EB00B66551 /* LensSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LensSpec.swift; sourceTree = "<group>"; };
82DD8EE31B48DDB200B66551 /* PrismSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrismSpec.swift; sourceTree = "<group>"; };
82DD8EE61B48DF8700B66551 /* IsoSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IsoSpec.swift; sourceTree = "<group>"; };
82DD8EFC1B4CE0E600B66551 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Carthage/Checkouts/Operadics/Operators.swift; sourceTree = SOURCE_ROOT; };
82DD8EDE1B48D2EB00B66551 /* LensSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LensSpec.swift; sourceTree = "<group>"; usesTabs = 1; };
82DD8EE31B48DDB200B66551 /* PrismSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrismSpec.swift; sourceTree = "<group>"; usesTabs = 1; };
82DD8EE61B48DF8700B66551 /* IsoSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IsoSpec.swift; sourceTree = "<group>"; usesTabs = 1; };
82DD8EFC1B4CE0E600B66551 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Operators.swift; path = Carthage/Checkouts/Operadics/Operators.swift; sourceTree = SOURCE_ROOT; usesTabs = 1; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -208,6 +211,7 @@
82DD8EFC1B4CE0E600B66551 /* Operators.swift */,
824B46301B460B25009AAF49 /* Lens.swift */,
824B462B1B460B25009AAF49 /* Iso.swift */,
11D137041B544D0000E76EBA /* Protocols.swift */,
824B46311B460B25009AAF49 /* Prism.swift */,
824B462C1B460B25009AAF49 /* IxCont.swift */,
824B462D1B460B25009AAF49 /* IxMultiStore.swift */,
Expand Down Expand Up @@ -484,6 +488,7 @@
824B46361B460B25009AAF49 /* IxMultiStore.swift in Sources */,
82DD8EFD1B4CE0E600B66551 /* Operators.swift in Sources */,
824B463E1B460B25009AAF49 /* Prism.swift in Sources */,
11D137051B544D0000E76EBA /* Protocols.swift in Sources */,
824B46341B460B25009AAF49 /* IxCont.swift in Sources */,
824B46381B460B25009AAF49 /* IxState.swift in Sources */,
824B46321B460B25009AAF49 /* Iso.swift in Sources */,
Expand Down Expand Up @@ -513,6 +518,7 @@
824B46371B460B25009AAF49 /* IxMultiStore.swift in Sources */,
82DD8EFE1B4CE0E600B66551 /* Operators.swift in Sources */,
824B463F1B460B25009AAF49 /* Prism.swift in Sources */,
11D137061B544D0000E76EBA /* Protocols.swift in Sources */,
824B46351B460B25009AAF49 /* IxCont.swift in Sources */,
824B46391B460B25009AAF49 /* IxState.swift in Sources */,
824B46331B460B25009AAF49 /* Iso.swift in Sources */,
Expand Down
83 changes: 60 additions & 23 deletions Focus/Iso.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,41 @@
/// - parameter T: The target of the Iso heading left
/// - parameter A: The source of the Iso heading right
/// - parameter B: The target of the Iso heading left
public struct Iso<S, T, A, B> {
public let get : S -> A
public let inject : B -> T
public struct Iso<S, T, A, B> : IsoType {
typealias Source = S
typealias Target = A
typealias AltSource = T
typealias AltTarget = B

private let _get : S -> A
private let _inject : B -> T

/// Builds an Iso from a pair of inverse functions.
public init(get : S -> A, inject : B -> T) {
self.get = get
self.inject = inject
public init(get f : S -> A, inject g : B -> T) {
_get = f
_inject = g
}

/// Runs a value of type `S` along both parts of the Iso.
public func modify(v : S, _ f : A -> B) -> T {
return inject(f(get(v)))
public func get(v : S) -> A {
return _get(v)
}

/// Composes an `Iso` with the receiver.
public func compose<I, J>(i2 : Iso<A, B, I, J>) -> Iso<S, T, I, J> {
return self i2
}

/// Converts an Iso to a Lens.
public var asLens : Lens<S, T, A, B> {
return Lens { s in IxStore(self.get(s)) { self.inject($0) } }
public func inject(x : B) -> T {
return _inject(x)
}

/// Converts an Iso to a Prism with a getter that always succeeds..
public var asPrism : Prism<S, T, A, B> {
return Prism(tryGet: { .Some(self.get($0)) }, inject: inject)
}

public protocol IsoType : OpticFamilyType, LensType, PrismType {
func get(_ : Source) -> Target
func inject(_ : AltTarget) -> AltSource
}

extension Iso {
public init<Other : IsoType where
S == Other.Source, A == Other.Target, T == Other.AltSource, B == Other.AltTarget>
(_ other : Other)
{
self.init(get: other.get, inject: other.inject)
}
}

Expand All @@ -48,7 +55,37 @@ public func identity<S, T>() -> Iso<S, T, S, T> {
return Iso(get: identity, inject: identity)
}

extension IsoType {
public func run(v : Source) -> IxStore<Target, AltTarget, AltSource> {
return IxStore<Target, AltTarget, AltSource>(get(v)) { x in
return self.inject(x)
}
}

public func tryGet(v : Source) -> Target? {
return get(v)
}

/// Runs a value of type `S` along both parts of the Iso.
public func modify(v : Source, _ f : Target -> AltTarget) -> AltSource {
return inject(f(get(v)))
}

/// Composes an `Iso` with the receiver.
public func compose<Other : IsoType where
Self.Target == Other.Source,
Self.AltTarget == Other.AltSource>
(other : Other) -> Iso<Source, AltSource, Other.Target, Other.AltTarget>
{
return Iso(get: other.get self.get, inject: self.inject other.inject)
}
}

/// Compose isomorphisms.
public func <S, T, I, J, A, B>(i1 : Iso<S, T, I, J>, i2 : Iso<I, J, A, B>) -> Iso<S, T, A, B> {
return Iso(get: i2.get i1.get, inject: i1.inject i2.inject)
public func <Left : IsoType, Right : IsoType where
Left.Target == Right.Source,
Left.AltTarget == Right.AltSource>
(l : Left, r : Right) -> Iso<Left.Source, Left.AltSource, Right.Target, Right.AltTarget>
{
return l.compose(r)
}
89 changes: 63 additions & 26 deletions Focus/Lens.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//

/// A Lens (or Functional Reference) describes a way of focusing on the parts of a structure,
/// composing with other lenses to focus deeper into a structure, and returning new structures with
/// parts modified. In this way, a Lens can be thought of as a reference to a subpart of a
/// composing with other lenses to focus deeper into a structure, and returning new structures with
/// parts modified. In this way, a Lens can be thought of as a reference to a subpart of a
/// structure.
///
/// A well-behaved Lens should obey the following laws:
Expand All @@ -29,13 +29,22 @@
/// - parameter T: The modified source of the Lens
/// - parameter A: The target of the Lens
/// - parameter B: The modified target the Lens
public struct Lens<S, T, A, B> {
public struct Lens<S, T, A, B> : LensType {
typealias Source = S
typealias Target = A
typealias AltSource = T
typealias AltTarget = B

/// Gets the Indexed Costate Comonad Coalgebroid underlying the receiver.
public let run : S -> IxStore<A, B, T>
private let _run : S -> IxStore<A, B, T>

public func run(v : S) -> IxStore<A, B, T> {
return _run(v)
}

/// Produces a lens from an Indexed Costate Comonad Coalgebroid.
public init(_ run : S -> IxStore<A, B, T>) {
self.run = run
public init(_ f : S -> IxStore<A, B, T>) {
_run = f
}

/// Creates a lens from a getter/setter pair.
Expand All @@ -47,60 +56,88 @@ public struct Lens<S, T, A, B> {
public init(get : S -> A, modify : (S, A -> B) -> T) {
self.init(get: get, set: { v, x in modify(v) { _ in x } })
}
}

/// Composes a `Lens` with the receiver.
public func compose<I, J>(l2 : Lens<A, B, I, J>) -> Lens<S, T, I, J> {
return self l2
public protocol LensType : OpticFamilyType {
/// Gets the Indexed Costate Comonad Coalgebroid underlying the receiver.
func run(_ : Source) -> IxStore<Target, AltTarget, AltSource>
}

extension Lens {
public init<Other : LensType where
S == Other.Source, A == Other.Target, T == Other.AltSource, B == Other.AltTarget>
(_ other : Other)
{
self.init(other.run)
}

}

extension LensType {
/// Runs the getter on a given structure.
public func get(v : S) -> A {
public func get(v : Source) -> Target {
return run(v).pos
}

/// Runs the setter on a given structure and value to yield a new structure.
public func set(v : S, _ x : B) -> T {
public func set(v : Source, _ x : AltTarget) -> AltSource {
return run(v).peek(x)
}

/// Transform the value of the retrieved field by a function.
public func modify(v : S, _ f : A -> B) -> T {
public func modify(v : Source, _ f : Target -> AltTarget) -> AltSource {
let q = run(v)
return q.peek(f(q.pos))
}

/// Composes a `Lens` with the receiver.
public func compose<Other : LensType where
Self.Target == Other.Source,
Self.AltTarget == Other.AltSource>
(other : Other) -> Lens<Source, AltSource, Other.Target, Other.AltTarget> {
return Lens { v in
let q1 = self.run(v)
let q2 = other.run(q1.pos)
return IxStore(q2.pos) { q1.peek(q2.peek($0)) }
}
}

/// Uses the receiver to focus in on a State Monad.
public func zoom<X>(a : IxState<A, B, X>) -> IxState<S, T, X> {
public func zoom<X>(a : IxState<Target, AltTarget, X>) -> IxState<Source, AltSource, X> {
return IxState { s1 in
let q = self.run(s1)
let (x, s2) = a.run(q.pos)
return (x, q.peek(s2))
}
}

/// Creates a Lens that focuses on two structures.
public func split<S_, T_, A_, B_>(right : Lens<S_, T_, A_, B_>) -> Lens<(S, S_), (T, T_), (A, A_), (B, B_)> {
return Lens<(S, S_), (T, T_), (A, A_), (B, B_)> { (vl, vr) in
public func split<Other : LensType>(right : Other) -> Lens<
(Source, Other.Source), (AltSource, Other.AltSource),
(Target, Other.Target), (AltTarget, Other.AltTarget)>
{
return Lens { (vl, vr) in
let q1 = self.run(vl)
let q2 = right.run(vr)
return IxStore((q1.pos, q2.pos)) { (l, r) in (q1.peek(l), q2.peek(r)) }
}
}

/// Creates a Lens that sends its input structure to both Lenses to focus on distinct subparts.
public func fanout<A_, T_>(right : Lens<S, T_, A_, B>) -> Lens<S, (T, T_), (A, A_), B> {
return Lens<S, (T, T_), (A, A_), B> { s in
public func fanout<Other : LensType
where Source == Other.Source, AltTarget == Other.AltTarget>
(right : Other) -> Lens<Source, (AltSource, Other.AltSource), (Target, Other.Target), AltTarget>
{
return Lens { s in
let q1 = self.run(s)
let q2 = right.run(s)
return IxStore((q1.pos, q2.pos)) { (q1.peek($0), q2.peek($0)) }
}
}
}

public func <S, T, I, J, A, B>(l1 : Lens<S, T, I, J>, l2 : Lens<I, J, A, B>) -> Lens<S, T, A, B> {
return Lens { v in
let q1 = l1.run(v)
let q2 = l2.run(q1.pos)
return IxStore(q2.pos) { q1.peek(q2.peek($0)) }
}
public func <Left : LensType, Right : LensType where
Left.Target == Right.Source,
Left.AltTarget == Right.AltSource>
(l : Left, r : Right) -> Lens<Left.Source, Left.AltSource, Right.Target, Right.AltTarget> {
return l.compose(r)
}
Loading

0 comments on commit 2125780

Please sign in to comment.