Skip to content

Commit

Permalink
Add PairK
Browse files Browse the repository at this point in the history
  • Loading branch information
ferranpujolcamins committed Nov 9, 2020
1 parent 738d6b4 commit 2a29ad1
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 1 deletion.
12 changes: 12 additions & 0 deletions Bow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,14 @@
B58D2CA32540029F0099D426 /* MonadTrans.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D2CA22540029F0099D426 /* MonadTrans.swift */; };
B58D2CFF254003900099D426 /* MonadTransLaws.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D2CA4254002AB0099D426 /* MonadTransLaws.swift */; };
B58D2D17254005A60099D426 /* ComonadTransLaws.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D2D16254005A60099D426 /* ComonadTransLaws.swift */; };
B598643E2558768500784952 /* PairK.swift in Sources */ = {isa = PBXBuildFile; fileRef = B598643D2558768500784952 /* PairK.swift */; };
B5BF5086252B0F4A0060AD0D /* LazyFunction1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF5085252B0F4A0060AD0D /* LazyFunction1.swift */; };
B5BF50F7252B24AF0060AD0D /* LazyFunction1Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF509D252B23D30060AD0D /* LazyFunction1Test.swift */; };
B5BF510F252B253D0060AD0D /* LazyFunction1+Gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5BF510E252B253D0060AD0D /* LazyFunction1+Gen.swift */; };
B5D4BF262550513600C1A661 /* MonadComprehensionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D4BF252550513600C1A661 /* MonadComprehensionTest.swift */; };
B5DFCB512553FCD2008D3546 /* Exists+Gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DFCB502553FCD2008D3546 /* Exists+Gen.swift */; };
B5FD89342559382A0027C335 /* PairKTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FD89332559382A0027C335 /* PairKTest.swift */; };
B5FD894D255939F60027C335 /* PairK+Gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FD894C255939F60027C335 /* PairK+Gen.swift */; };
B8B910C0234D7F2600E44271 /* Semiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B910BF234D7F2600E44271 /* Semiring.swift */; };
B8B910C4234D846900E44271 /* SemiringLaws.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B910C3234D846900E44271 /* SemiringLaws.swift */; };
B8B910C6234DDA4000E44271 /* BoolInstancesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B910C5234DDA4000E44271 /* BoolInstancesTest.swift */; };
Expand Down Expand Up @@ -1270,11 +1273,14 @@
B58D2CA22540029F0099D426 /* MonadTrans.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonadTrans.swift; sourceTree = "<group>"; };
B58D2CA4254002AB0099D426 /* MonadTransLaws.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonadTransLaws.swift; sourceTree = "<group>"; };
B58D2D16254005A60099D426 /* ComonadTransLaws.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComonadTransLaws.swift; sourceTree = "<group>"; };
B598643D2558768500784952 /* PairK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairK.swift; sourceTree = "<group>"; };
B5BF5085252B0F4A0060AD0D /* LazyFunction1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyFunction1.swift; sourceTree = "<group>"; };
B5BF509D252B23D30060AD0D /* LazyFunction1Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyFunction1Test.swift; sourceTree = "<group>"; };
B5BF510E252B253D0060AD0D /* LazyFunction1+Gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LazyFunction1+Gen.swift"; sourceTree = "<group>"; };
B5D4BF252550513600C1A661 /* MonadComprehensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonadComprehensionTest.swift; sourceTree = "<group>"; };
B5DFCB502553FCD2008D3546 /* Exists+Gen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Exists+Gen.swift"; sourceTree = "<group>"; };
B5FD89332559382A0027C335 /* PairKTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PairKTest.swift; sourceTree = "<group>"; };
B5FD894C255939F60027C335 /* PairK+Gen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PairK+Gen.swift"; sourceTree = "<group>"; };
B8B910BF234D7F2600E44271 /* Semiring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Semiring.swift; sourceTree = "<group>"; };
B8B910C3234D846900E44271 /* SemiringLaws.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemiringLaws.swift; sourceTree = "<group>"; };
B8B910C5234DDA4000E44271 /* BoolInstancesTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoolInstancesTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1488,6 +1494,7 @@
112097D122A8FFC0007F3D9C /* Moore+Gen.swift */,
112097BA22A7FC86007F3D9C /* NonEmptyArray+Gen.swift */,
112097BC22A7FD6F007F3D9C /* Option+Gen.swift */,
B5FD894C255939F60027C335 /* PairK+Gen.swift */,
112097D522A904AC007F3D9C /* Sum+Gen.swift */,
B578BCA925385B9200B8FD42 /* Trampoline+Gen.swift */,
B5618784252CC3EA002717B1 /* Tree+Gen.swift */,
Expand Down Expand Up @@ -1983,6 +1990,7 @@
113746A223435BA300D9C1AD /* DictionaryKTest.swift */,
1137469F234359C000D9C1AD /* DictionaryTest.swift */,
8BA0F3A9217E2DEF00969984 /* EitherKTest.swift */,
B5FD89332559382A0027C335 /* PairKTest.swift */,
8BA0F3AB217E2DEF00969984 /* EitherTest.swift */,
720C1E0323FD80AB001C5B7D /* EndoTest.swift */,
B55F52B125247256001979EE /* ExistsTests.swift */,
Expand Down Expand Up @@ -2121,6 +2129,7 @@
8BA0F479217E2E9200969984 /* NonEmptyArray.swift */,
8BA0F478217E2E9200969984 /* Option.swift */,
09CEF850236E28680070CF43 /* Pairing.swift */,
B598643D2558768500784952 /* PairK.swift */,
1123686E240FC316005D4CF4 /* Puller.swift */,
8BA0F473217E2E9200969984 /* Reader.swift */,
8BA0F482217E2E9200969984 /* Result.swift */,
Expand Down Expand Up @@ -2929,6 +2938,7 @@
112097DD22A90899007F3D9C /* OptionT+Gen.swift in Sources */,
B578BCAA25385B9200B8FD42 /* Trampoline+Gen.swift in Sources */,
112097B122A7F0E6007F3D9C /* ArrayK+Gen.swift in Sources */,
B5FD894D255939F60027C335 /* PairK+Gen.swift in Sources */,
112097BF22A7FDEF007F3D9C /* Try+Gen.swift in Sources */,
112097D222A8FFC0007F3D9C /* Moore+Gen.swift in Sources */,
112097CC22A819E9007F3D9C /* Day+Gen.swift in Sources */,
Expand Down Expand Up @@ -3317,6 +3327,7 @@
8BA0F3E8217E2DEF00969984 /* BooleanFunctionsTest.swift in Sources */,
B58B9BE925481DB4008BA4A7 /* YonedaTest.swift in Sources */,
8BA0F3FD217E2DEF00969984 /* CurryTest.swift in Sources */,
B5FD89342559382A0027C335 /* PairKTest.swift in Sources */,
119D053323D9E8B400F0D559 /* PairingTest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -3394,6 +3405,7 @@
115488CF23BF9FEF00A6DE92 /* ComonadTrans.swift in Sources */,
8BA0F5A3217E2E9200969984 /* Moore.swift in Sources */,
110DE5CC23ACE29D00E5DB36 /* ComonadStore.swift in Sources */,
B598643E2558768500784952 /* PairK.swift in Sources */,
606B29EA23607607002334B2 /* Divide.swift in Sources */,
8BA0F5AF217E2E9200969984 /* Try.swift in Sources */,
B57738FD252F21CC003B3EBF /* Coyoneda.swift in Sources */,
Expand Down
176 changes: 176 additions & 0 deletions Sources/Bow/Data/PairK.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import Foundation

/// Witness for the `PairK<F, G, A>` data type. To be used in simulated Higher Kinded Types.
public final class ForPairK {}

/// Partial application of the `PairK` type constructor, omitting the last parameter.
public final class PairKPartial<F, G>: Kind2<ForPairK, F, G> {}

/// Higher Kinded Type alias to improve readability over `Kind<PairKPartial<F, G>, A>`.
public typealias PairKOf<F, G, A> = Kind<PairKPartial<F, G>, A>

/// `PairK` is a product type for kinds. Represents a type where you hold both a `Kind<F, A>` and a `Kind<G, A>`.
public final class PairK<F, G, A>: PairKOf<F, G, A> {
fileprivate let run: Pair<Kind<F, A>, Kind<G, A>>

/// Safe downcast.
///
/// - Parameter fa: Value in the higher-kind form.
/// - Returns: Value cast to `PairK`.
public static func fix(_ fa: PairKOf<F, G, A>) -> PairK<F, G, A> {
fa as! PairK<F, G, A>
}

/// Initialises a `PairK` from two values.
///
/// - Parameters:
/// - fa: Value of the first component of the pair.
/// - ga: Value of the second component of the pair.
public init(_ fa: Kind<F, A>, _ ga: Kind<G, A>) {
self.run = Pair(fa, ga)
}

/// Initialises a `PairK` from a `Pair`.
///
/// - Parameter pair: `Pair` value to initialise this `PairK`.
public init(_ pair: Pair<Kind<F, A>, Kind<G, A>>) {
self.run = pair
}

/// The value of the first component of this pair.
var first: Kind<F, A> {
run.first
}

/// The value of the second component of this pair.
var second: Kind<G, A> {
run.second
}
}

/// Safe downcast.
///
/// - Parameter fa: Value in higher-kind form.
/// - Returns: Value cast to `PairK`.
public postfix func ^ <F, G, A>(_ fa: PairKOf<F, G, A>) -> PairK<F, G, A> {
PairK.fix(fa)
}

extension PairK where F: Applicative, G: Applicative {
/// Initialises a `PairK` from the provided values lifting them to `F` and `G`.
///
/// - Parameters:
/// - a1: Value of the first component of the pair.
/// - a2: Value of the second component of the pair.
public convenience init(_ a1: A, _ a2: A) {
self.init(F.pure(a1), G.pure(a2))
}
}

// MARK: Instance of EquatableK for PairK.
extension PairKPartial: EquatableK where F: EquatableK, G: EquatableK {
public static func eq<A>(_ lhs: PairKOf<F, G, A>, _ rhs: PairKOf<F, G, A>) -> Bool where A : Equatable {
lhs^.first == rhs^.first && lhs^.second == rhs^.second
}
}

// MARK: Instance of HashableK for PairK.
extension PairKPartial: HashableK where F: HashableK, G: HashableK {
public static func hash<A>(_ fa: PairKOf<F, G, A>, into hasher: inout Hasher) where A : Hashable {
hasher.combine(fa^.run)
}
}

// MARK: Instance of Invariant for PairK.
extension PairKPartial: Invariant where F: Functor, G: Functor {}

// MARK: Instance of Functor for PairK.
extension PairKPartial: Functor where F: Functor, G: Functor {
public static func map<A, B>(_ fa: PairKOf<F, G, A>, _ f: @escaping (A) -> B) -> PairKOf<F, G, B> {
PairK(fa^.run.bimap({ F.map($0, f) }, { G.map($0, f) }))
}
}

// MARK: Instance of Applicative for PairK.
extension PairKPartial: Applicative where F: Applicative, G: Applicative {
public static func pure<A>(_ a: A) -> PairKOf<F, G, A> {
PairK(a, a)
}

public static func ap<A, B>(_ ff: PairKOf<F, G, (A) -> B>, _ fa: PairKOf<F, G, A>) -> PairKOf<F, G, B> {
PairK(
F.ap(ff^.first, fa^.first),
G.ap(ff^.second, fa^.second)
)
}
}

// MARK: Instance of Selective for PairK
extension PairKPartial: Selective where F: Monad, G: Monad {}

// MARK: Instance of Monad for PairK
extension PairKPartial: Monad where F: Monad, G: Monad {
public static func flatMap<A, B>(_ fa: PairKOf<F, G, A>, _ f: @escaping (A) -> PairKOf<F, G, B>) -> PairKOf<F, G, B> {
PairK(
F.flatMap(fa^.first) { f($0)^.first },
G.flatMap(fa^.second) { f($0)^.second }
)
}

public static func tailRecM<A, B>(_ a: A, _ f: @escaping (A) -> PairKOf<F, G, Either<A, B>>) -> PairKOf<F, G, B> {
PairK(
F.tailRecM(a, { f($0)^.first }),
G.tailRecM(a, { f($0)^.second })
)
}
}

// MARK: Instance of FunctorFilter for PairK
extension PairKPartial: FunctorFilter where F: MonadFilter, G: MonadFilter {}

// MARK: Instance of MonadFilter for PairK
extension PairKPartial: MonadFilter where F: MonadFilter, G: MonadFilter {
public static func empty<A>() -> PairKOf<F, G, A> {
PairK(F.empty(), G.empty())
}
}

// MARK: Instance of SemigroupK for PairK
extension PairKPartial: SemigroupK where F: SemigroupK, G: SemigroupK {
public static func combineK<A>(
_ x: PairKOf<F, G, A>,
_ y: PairKOf<F, G, A>) -> PairKOf<F, G, A> {
PairK(
x^.first.combineK(y^.first),
x^.second.combineK(y^.second)
)
}
}

// MARK: Instance of MonoidK for PairK
extension PairKPartial: MonoidK where F: MonoidK, G: MonoidK {
public static func emptyK<A>() -> PairKOf<F, G, A> {
PairK(F.emptyK(), G.emptyK())
}
}

// MARK: Instance of ApplicativeError for PairK
extension PairKPartial: ApplicativeError where F: MonadError, G: MonadError, F.E == G.E {
public typealias E = F.E

public static func raiseError<A>(_ e: E) -> PairKOf<F, G, A> {
PairK(F.raiseError(e), G.raiseError(e))
}

public static func handleErrorWith<A>(
_ fa: PairKOf<F, G, A>,
_ f: @escaping (E) -> PairKOf<F, G, A>) -> PairKOf<F, G, A> {
PairK(
fa^.first.handleErrorWith { f($0)^.first },
fa^.second.handleErrorWith { f($0)^.second }
)
}
}

// MARK: Instance of MonadError for PairK
extension PairKPartial: MonadError where F: MonadError, G: MonadError, F.E == G.E {}
2 changes: 1 addition & 1 deletion Sources/Bow/Transformers/WriterT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ extension WriterTPartial: ApplicativeError where F: MonadError, W: Monoid {
}
}

// MARK: Instance of MonadError for WriterT`
// MARK: Instance of MonadError for WriterT
extension WriterTPartial: MonadError where F: MonadError, W: Monoid {}

// MARK: Instance of MonadReader for WriterT
Expand Down
18 changes: 18 additions & 0 deletions Tests/BowGenerators/Data/PairK+Gen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Bow
import SwiftCheck

// MARK: Generator for Property-based Testing

extension PairK: Arbitrary where F: ArbitraryK, G: ArbitraryK, A: Arbitrary {
public static var arbitrary: Gen<PairK<F, G, A>> {
Gen.from(PairKPartial.generate >>> PairK.fix)
}
}

// MARK: Instance of ArbitraryK for PairK

extension PairKPartial: ArbitraryK where F: ArbitraryK, G: ArbitraryK {
public static func generate<A: Arbitrary>() -> PairKOf<F, G, A> {
PairK(F.generate(), G.generate())
}
}
51 changes: 51 additions & 0 deletions Tests/BowTests/Data/PairKTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import XCTest
import BowLaws
import Bow
import BowGenerators
import SwiftCheck

class PairKTest: XCTestCase {
func testEquatableLaws() {
EquatableKLaws<PairKPartial<ForId, ForId>, Int>.check()
}

func testHashableLaws() {
HashableKLaws<PairKPartial<ForId, ForId>, Int>.check()
}

func testInvariantLaws() {
InvariantLaws<PairKPartial<ForId, ForId>>.check()
}

func testFunctorLaws() {
FunctorLaws<PairKPartial<ForId, ForId>>.check()
}

func testApplicativeLaws() {
ApplicativeLaws<PairKPartial<ForId, ForId>>.check()
}

func testFunctorFilterLaws() {
FunctorFilterLaws<PairKPartial<ForOption, ForOption>>.check()
}

func testMonadFilterLaws() {
MonadFilterLaws<PairKPartial<ForOption, ForOption>>.check()
}

func testSemigroupKLaws() {
SemigroupKLaws<PairKPartial<ForOption, ForOption>>.check()
}

func testMonoidKLaws() {
SemigroupKLaws<PairKPartial<ForOption, ForOption>>.check()
}

func testApplicativeErrorLaws() {
ApplicativeErrorLaws<PairKPartial<EitherPartial<Int>, EitherPartial<Int>>>.check()
}

func testMonadErrorLaws() {
MonadErrorLaws<PairKPartial<EitherPartial<Int>, EitherPartial<Int>>>.check()
}
}

0 comments on commit 2a29ad1

Please sign in to comment.