Skip to content

Commit

Permalink
Merge pull request #21 from CodaFi/power-welding
Browse files Browse the repository at this point in the history
A Little Cleanup
  • Loading branch information
pthariensflame committed Dec 23, 2015
2 parents 02982a5 + 52090cb commit e39ac8c
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode7.1
osx_image: xcode7.2
before_install: true
install: true
script: script/cibuild Focus Focus-iOS
Expand Down
6 changes: 3 additions & 3 deletions Focus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,15 @@
824B46411B460BE2009AAF49 /* ArrayZipper.swift */,
824B46471B460C1E009AAF49 /* LensOperators.swift */,
82DD8EFC1B4CE0E600B66551 /* Operators.swift */,
824B46301B460B25009AAF49 /* Lens.swift */,
824B462B1B460B25009AAF49 /* Iso.swift */,
11D137041B544D0000E76EBA /* Protocols.swift */,
82E93EB01B8A7907003D5798 /* Monomorphic.swift */,
824B46301B460B25009AAF49 /* Lens.swift */,
824B46311B460B25009AAF49 /* Prism.swift */,
824B462B1B460B25009AAF49 /* Iso.swift */,
824B462C1B460B25009AAF49 /* IxCont.swift */,
824B462D1B460B25009AAF49 /* IxMultiStore.swift */,
824B462E1B460B25009AAF49 /* IxState.swift */,
824B462F1B460B25009AAF49 /* IxStore.swift */,
82E93EB01B8A7907003D5798 /* Monomorphic.swift */,
824B46401B460B28009AAF49 /* Supporting Files */,
);
path = Focus;
Expand Down
37 changes: 32 additions & 5 deletions Focus/Iso.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@
// Copyright (c) 2015 TypeLift. All rights reserved.
//

/// Captures an isomorphism between S and A.
/// Captures an isomorphism between S, A and B, T.
///
/// - parameter S: The source of the Iso heading right
/// - 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
/// In practice, an `Iso` is used with two structures that can be converted between each other
/// without information loss. For example, the Isomorphism between `Optional<T>` and
/// `ImplicitlyUnwrappedOptional<T>` is expressed as
///
/// Iso<Optional<T>, Optional<U>, ImplicitlyUnwrappedOptional<T>, ImplicitlyUnwrappedOptional<U>>
///
/// If a less-powerful form of `Iso` is needed, where `S == T` and `A == B`, consider using a
/// `SimpleIso` instead.
///
/// - parameter S: The source of the first function of the isomorphism.
/// - parameter T: The target of the second function of the isomorphism.
/// - parameter A: The target of the first function of the isomorphism.
/// - parameter B: The source of the second function of the isomorphism.
public struct Iso<S, T, A, B> : IsoType {
public typealias Source = S
public typealias Target = A
Expand All @@ -27,15 +36,18 @@ public struct Iso<S, T, A, B> : IsoType {
_inject = g
}

/// Extracts the first function from the isomorphism.
public func get(v : S) -> A {
return _get(v)
}

/// Extracts the second function from the isomorphism.
public func inject(x : B) -> T {
return _inject(x)
}
}

/// Captures the essential structure of an `Iso`.
public protocol IsoType : OpticFamilyType, LensType, PrismType {
func get(_ : Source) -> Target
func inject(_ : AltTarget) -> AltSource
Expand All @@ -62,6 +74,7 @@ extension IsoType {
}
}

/// An `Iso`'s `tryGet` will always succeed.
public func tryGet(v : Source) -> Target? {
return get(v)
}
Expand All @@ -79,6 +92,20 @@ extension IsoType {
{
return Iso(get: other.get self.get, inject: self.inject other.inject)
}

/// Extracts the two functions that characterize the receiving `Iso`.
public func withIso<R>(k : ((Source -> Target), (AltTarget -> AltSource)) -> R) -> R {
return k(self.get, self.inject)
}

/// Returns the inverse `Iso` from the receiver.
///
/// self.invert.invert == self
public var invert : Iso<AltTarget, Target, AltSource, Source> {
return self.withIso { sa, bt in
return Iso<AltTarget, Target, AltSource, Source>(get: bt, inject: sa)
}
}
}

/// Compose isomorphisms.
Expand Down
32 changes: 23 additions & 9 deletions Focus/Lens.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@
// Copyright (c) 2015 TypeLift. All rights reserved.
//

/// A Lens (or Functional Reference) describes a way of focusing on the parts of a structure,
/// 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
/// 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:
/// In practice, a `Lens` is used with Product structures like tuples, classes, and structs. If a
/// less-powerful form of `Lens` is needed, consider using a `SimpleLens` instead.
///
/// A Lens, in its simplest form, can also be seen as a pair of functions:
///
/// - `get` to retrieve a focused part of the structure.
/// - `set` to replace focused parts and yield a new modified structure.
///
/// A well-behaved `Lens` should obey the following laws:
///
/// - You get back what you put in:
///
Expand All @@ -25,10 +33,10 @@
///
/// l.set(l.set(s, a), b) == l.set(s, b)
///
/// - parameter S: The source of the Lens
/// - parameter T: The modified source of the Lens
/// - parameter A: The target of the Lens
/// - parameter B: The modified target the Lens
/// - parameter S: The structure to be focused.
/// - parameter T: The modified form of the structure.
/// - parameter A: The result of retrieving the focused subpart.
/// - parameter B: The modification to make to the original structure.
public struct Lens<S, T, A, B> : LensType {
public typealias Source = S
public typealias Target = A
Expand All @@ -38,6 +46,7 @@ public struct Lens<S, T, A, B> : LensType {
/// Gets the Indexed Costate Comonad Coalgebroid underlying the receiver.
private let _run : S -> IxStore<A, B, T>

/// Runs the lens on a structure to retrieve the underlying Indexed Costate Comonad Coalgebroid.
public func run(v : S) -> IxStore<A, B, T> {
return _run(v)
}
Expand All @@ -58,6 +67,7 @@ public struct Lens<S, T, A, B> : LensType {
}
}

/// Captures the essential structure of a Lens.
public protocol LensType : OpticFamilyType {
/// Gets the Indexed Costate Comonad Coalgebroid underlying the receiver.
func run(_ : Source) -> IxStore<Target, AltTarget, AltSource>
Expand Down Expand Up @@ -110,7 +120,7 @@ extension LensType {
}
}

/// Creates a Lens that focuses on two structures.
/// Creates a `Lens` that focuses on two structures.
public func split<Other : LensType>(right : Other) -> Lens<
(Source, Other.Source), (AltSource, Other.AltSource),
(Target, Other.Target), (AltTarget, Other.AltTarget)>
Expand All @@ -122,7 +132,7 @@ extension LensType {
}
}

/// Creates a Lens that sends its input structure to both Lenses to focus on distinct subparts.
/// Creates a `Lens` that sends its input structure to both Lenses to focus on distinct subparts.
public func fanout<Other : LensType
where Source == Other.Source, AltTarget == Other.AltTarget>
(right : Other) -> Lens<Source, (AltSource, Other.AltSource), (Target, Other.Target), AltTarget>
Expand All @@ -135,6 +145,10 @@ extension LensType {
}
}

/// Composes two lenses to yield a "more focused" lens.
///
/// `Lens` composition occurs like property notation, in that more specific lenses come last rather
/// than first as they would under traditional function composition.
public func <Left : LensType, Right : LensType where
Left.Target == Right.Source,
Left.AltTarget == Right.AltSource>
Expand Down
24 changes: 22 additions & 2 deletions Focus/Prism.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,25 @@
// Copyright (c) 2015 TypeLift. All rights reserved.
//

/// A Prism is an `Iso` where one of the functions is partial.
/// A `Prism` describes a way of focusing on potentially more than one target structure. Like
/// an `Iso` a `Prism` is invertible, but unlike an `Iso` multi-focus `Prism`s are incompatible with
/// inversion in general. Because of this a `Prism` can branch depending on whether it hits its
/// target, much like pattern-matching in a `switch` statement.
///
/// An famous example of a `Prism` is
///
/// Prism<Optional<T>, Optional<T>, T, T>
///
/// provided by the `_Some` `Prism` in this library.
///
/// In practice, a `Prism` is used with Sum structures like enums. If a less-powerful form of
/// `Prism` is needed, where `S == T` and `A == B`, consider using a `SimplePrism` instead.
///
/// A Prism can thought of as an `Iso` characterized by two functions (where one of the functions is
/// partial):
///
/// - `tryGet` to possibly retrieve a focused part of the structure.
/// - `inject` to perform a "reverse get" back to a modified form of the original structure.
///
/// - parameter S: The source of the Prism
/// - parameter T: The modified source of the Prism
Expand All @@ -26,10 +44,12 @@ public struct Prism<S, T, A, B> : PrismType {
_inject = g
}

/// Attempts to focus the prism on the given source.
public func tryGet(v : Source) -> Target? {
return _tryGet(v)
}

/// Injects a value back into a modified form of the original structure.
public func inject(x : AltTarget) -> AltSource {
return _inject(x)
}
Expand All @@ -55,7 +75,7 @@ public func _Some<A, B>() -> Prism<A?, B?, A, B> {
}

/// Provides a Prism for traversing `.None`.
public func _None<A, B>() -> Prism<A?, B?, A, B> {
public func _None<A>() -> Prism<A?, A?, (), ()> {
return Prism(tryGet: { _ in .None }, inject: { _ in .None })
}

Expand Down
62 changes: 62 additions & 0 deletions FocusTests/IsoSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,37 @@ class IsoSpec : XCTestCase {
return iso1.inject(iso2.inject(x)) == isoC.inject(x)
}
}

func testIsoInverseLaws() {
property("around the world") <- forAll { (x : UInt, fs : IsoOf<Int, UInt>) in
let iso = Iso(get: fs.getTo, inject: fs.getFrom).invert.invert
return iso.get(iso.inject(x)) == x
}

property("and back again") <- forAll { (x : Int, fs : IsoOf<Int, UInt>) in
let iso = Iso(get: fs.getTo, inject: fs.getFrom).invert.invert
return iso.inject(iso.get(x)) == x
}

property("modify-identity") <- forAll { (x : Int, fs : IsoOf<Int, UInt>) in
let iso = Iso<Int, Int, UInt, UInt>(get: fs.getTo, inject: fs.getFrom).invert.invert
return iso.modify(x, { $0 }) == x
}

property("compose is associative (get)") <- forAll { (x : Int, fs : IsoOf<Int, Int>, gs : IsoOf<Int, Int>) in
let iso1 = Iso(get: fs.getTo, inject: fs.getFrom).invert.invert
let iso2 = Iso(get: gs.getTo, inject: gs.getFrom).invert.invert
let isoC = iso1.compose(iso2)
return iso2.get(iso1.get(x)) == isoC.get(x)
}

property("compose is associative (inject)") <- forAll { (x : Int, fs : IsoOf<Int, Int>, gs : IsoOf<Int, Int>) in
let iso1 = Iso(get: fs.getTo, inject: fs.getFrom).invert.invert
let iso2 = Iso(get: gs.getTo, inject: gs.getFrom).invert.invert
let isoC = iso1.compose(iso2)
return iso1.inject(iso2.inject(x)) == isoC.inject(x)
}
}
}


Expand Down Expand Up @@ -75,4 +106,35 @@ class SimpleIsoSpec : XCTestCase {
return iso1.inject(iso2.inject(x)) == isoC.inject(x)
}
}

func testIsoInverseLaws() {
property("around the world") <- forAll { (x : UInt, fs : IsoOf<Int, UInt>) in
let iso = SimpleIso(get: fs.getTo, inject: fs.getFrom).invert.invert
return iso.get(iso.inject(x)) == x
}

property("and back again") <- forAll { (x : Int, fs : IsoOf<Int, UInt>) in
let iso = SimpleIso(get: fs.getTo, inject: fs.getFrom).invert.invert
return iso.inject(iso.get(x)) == x
}

property("modify-identity") <- forAll { (x : Int, fs : IsoOf<Int, UInt>) in
let iso = Iso<Int, Int, UInt, UInt>(get: fs.getTo, inject: fs.getFrom).invert.invert
return iso.modify(x, { $0 }) == x
}

property("compose is associative (get)") <- forAll { (x : Int, fs : IsoOf<Int, Int>, gs : IsoOf<Int, Int>) in
let iso1 = SimpleIso(get: fs.getTo, inject: fs.getFrom).invert.invert
let iso2 = SimpleIso(get: gs.getTo, inject: gs.getFrom).invert.invert
let isoC = iso1.compose(iso2)
return iso2.get(iso1.get(x)) == isoC.get(x)
}

property("compose is associative (inject)") <- forAll { (x : Int, fs : IsoOf<Int, Int>, gs : IsoOf<Int, Int>) in
let iso1 = SimpleIso(get: fs.getTo, inject: fs.getFrom).invert.invert
let iso2 = SimpleIso(get: gs.getTo, inject: gs.getFrom).invert.invert
let isoC = iso1.compose(iso2)
return iso1.inject(iso2.inject(x)) == isoC.inject(x)
}
}
}

0 comments on commit e39ac8c

Please sign in to comment.