Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PairK #651

Merged
merged 4 commits into from
Nov 18, 2020
Merged

PairK #651

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 */; };
B5FD89342559382A0027C335 /* PairKTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FD89332559382A0027C335 /* PairKTest.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 */; };
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>"; };
B5FD89332559382A0027C335 /* PairKTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PairKTest.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>"; };
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
194 changes: 194 additions & 0 deletions Sources/Bow/Data/PairK.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
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: Invariant, G: Invariant {
public static func imap<A, B>(_ fa: PairKOf<F, G, A>, _ f: @escaping (A) -> B, _ g: @escaping (B) -> A) -> PairKOf<F, G, B> {
PairK(fa^.run.bimap({ F.imap($0, f, g) }, { G.imap($0, f, g) }))
}
}

// 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: Selective, G: Selective {
public static func select<A, B>(_ fab: PairKOf<F, G, Either<A, B>>, _ f: PairKOf<F, G, (A) -> B>) -> PairKOf<F, G, B> {
PairK(
F.select(fab^.first, f^.first),
G.select(fab^.second, f^.second)
)
}
}

// 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: FunctorFilter, G: FunctorFilter {
public static func mapFilter<A, B>(_ fa: PairKOf<F, G, A>, _ f: @escaping (A) -> OptionOf<B>) -> PairKOf<F, G, B> {
PairK(
F.mapFilter(fa^.first, f),
G.mapFilter(fa^.second, f)
)
}
}

// 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()
}
}