├── .github ├── ISSUE_TEMPLATE │ ├── bug.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── swift.yml ├── .gitignore ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── BowLite │ └── Headers.swift ├── BowLiteCore │ ├── Data │ │ ├── Array │ │ │ ├── Array+Applicative.swift │ │ │ ├── Array+Foldable.swift │ │ │ ├── Array+Functor.swift │ │ │ ├── Array+FunctorFilter.swift │ │ │ ├── Array+Monad.swift │ │ │ ├── Array+Traverse.swift │ │ │ └── Array+Utilities.swift │ │ ├── Const │ │ │ ├── Const+Applicative.swift │ │ │ ├── Const+Functor.swift │ │ │ └── Const.swift │ │ ├── Either │ │ │ ├── Either+Applicative.swift │ │ │ ├── Either+ApplicativeError.swift │ │ │ ├── Either+Functor.swift │ │ │ ├── Either+Monad.swift │ │ │ └── Either.swift │ │ ├── Eval │ │ │ ├── Eval+Applicative.swift │ │ │ ├── Eval+Comonad.swift │ │ │ ├── Eval+Functor.swift │ │ │ ├── Eval+Monad.swift │ │ │ └── Eval.swift │ │ ├── Function │ │ │ ├── Function+Applicative.swift │ │ │ ├── Function+Contravariant.swift │ │ │ ├── Function+Functor.swift │ │ │ ├── Function+Monad.swift │ │ │ ├── Function+MonadReader.swift │ │ │ └── Function.swift │ │ ├── Id │ │ │ ├── Id+Applicative.swift │ │ │ ├── Id+Functor.swift │ │ │ ├── Id+Monad.swift │ │ │ └── Id.swift │ │ ├── Ior │ │ │ ├── Ior+Applicative.swift │ │ │ ├── Ior+Functor.swift │ │ │ ├── Ior+Monad.swift │ │ │ └── Ior.swift │ │ ├── Kleilsi │ │ │ └── Kleisli.swift │ │ ├── NonEmptyArray │ │ │ ├── NonEmptyArray+Applicative.swift │ │ │ ├── NonEmptyArray+Foldable.swift │ │ │ ├── NonEmptyArray+Functor.swift │ │ │ ├── NonEmptyArray+FunctorFilter.swift │ │ │ ├── NonEmptyArray+Monad.swift │ │ │ ├── NonEmptyArray+Traverse.swift │ │ │ └── NonEmptyArray.swift │ │ ├── Optional │ │ │ ├── Optional+Applicative.swift │ │ │ ├── Optional+ApplicativeError.swift │ │ │ ├── Optional+Functor.swift │ │ │ ├── Optional+FunctorFilter.swift │ │ │ ├── Optional+Monad.swift │ │ │ └── Optional+Utilities.swift │ │ ├── Pair │ │ │ ├── Pair+Applicative.swift │ │ │ ├── Pair+Functor.swift │ │ │ ├── Pair+Monad.swift │ │ │ └── Pair.swift │ │ ├── Result │ │ │ ├── Result+Applicative.swift │ │ │ ├── Result+ApplicativeError.swift │ │ │ ├── Result+Functor.swift │ │ │ ├── Result+Monad.swift │ │ │ └── Result+Utilities.swift │ │ ├── State │ │ │ ├── State+Applicative.swift │ │ │ ├── State+Functor.swift │ │ │ ├── State+Monad.swift │ │ │ ├── State+MonadState.swift │ │ │ └── State.swift │ │ ├── Store │ │ │ ├── Store+Applicative.swift │ │ │ ├── Store+Comonad.swift │ │ │ ├── Store+ComonadStore.swift │ │ │ ├── Store+Functor.swift │ │ │ └── Store.swift │ │ ├── Trampoline │ │ │ └── Trampoline.swift │ │ ├── Try │ │ │ ├── Try+Applicative.swift │ │ │ ├── Try+ApplicativeError.swift │ │ │ ├── Try+Functor.swift │ │ │ ├── Try+Monad.swift │ │ │ └── Try.swift │ │ ├── Validated │ │ │ ├── Validated+Applicative.swift │ │ │ ├── Validated+ApplicativeError.swift │ │ │ ├── Validated+Functor.swift │ │ │ └── Validated.swift │ │ └── Writer │ │ │ ├── Writer+Applicative.swift │ │ │ ├── Writer+Functor.swift │ │ │ ├── Writer+Monad.swift │ │ │ ├── Writer+MonadWriter.swift │ │ │ └── Writer.swift │ ├── Instances │ │ ├── BoolInstances.swift │ │ ├── NumberInstances.swift │ │ └── StringInstances.swift │ ├── Syntax │ │ ├── BooleanFunctions.swift │ │ ├── Composition.swift │ │ ├── Curry.swift │ │ ├── Memoization.swift │ │ ├── PartialApplication.swift │ │ ├── Predef.swift │ │ └── Reverse.swift │ └── Typeclasses │ │ ├── Monoid.swift │ │ ├── Semigroup.swift │ │ └── Semiring.swift ├── BowLiteEffects │ ├── Atomic.swift │ ├── Data │ │ ├── Array+ConcurrentTraverse.swift │ │ ├── Array+TraverseIO.swift │ │ ├── NonEmptyArray+ConcurrentTraverse.swift │ │ └── NonEmptyArray+TraverseIO.swift │ ├── EnvIO │ │ ├── EnvIO+Applicative.swift │ │ ├── EnvIO+ApplicativeError.swift │ │ ├── EnvIO+Async.swift │ │ ├── EnvIO+Concurrent.swift │ │ ├── EnvIO+Functor.swift │ │ ├── EnvIO+Monad.swift │ │ ├── EnvIO+MonadDefer.swift │ │ ├── EnvIO+MonadReader.swift │ │ └── EnvIO.swift │ ├── Foundation │ │ ├── ConsoleIO.swift │ │ ├── FileManager+Common.swift │ │ ├── FileManager+iOS+Mac.swift │ │ └── URLSession.swift │ ├── IO │ │ ├── IO+Applicative.swift │ │ ├── IO+ApplicativeError.swift │ │ ├── IO+Async.swift │ │ ├── IO+Bracket.swift │ │ ├── IO+Concurrent.swift │ │ ├── IO+Functor.swift │ │ ├── IO+Monad.swift │ │ ├── IO+MonadDefer.swift │ │ └── IO.swift │ ├── Internal │ │ ├── DispatchTimeInterval+Extensions.swift │ │ └── Queue.swift │ ├── Kleisli │ │ └── Kleisli+IO.swift │ ├── MonadComprehensions │ │ ├── BindingOperator.swift │ │ ├── EnvIO │ │ │ ├── EnvIOBindingExpression.swift │ │ │ ├── EnvIOBindingOperator.swift │ │ │ ├── EnvIOBoundVar.swift │ │ │ ├── EnvIOConcurrent.swift │ │ │ └── EnvIOMonadComprehensions.swift │ │ └── IO │ │ │ ├── IOBindingExpression.swift │ │ │ ├── IOBindingOperator.swift │ │ │ ├── IOBoundVar.swift │ │ │ ├── IOConcurrent.swift │ │ │ └── IOMonadComprehensions.swift │ ├── Ref.swift │ ├── Resource.swift │ └── Schedule.swift └── BowLiteOptics │ ├── AffineTraversal.swift │ ├── Lens.swift │ ├── Prism.swift │ └── Traversal.swift ├── Tests ├── BowLiteCoreTests │ ├── Data │ │ ├── Array │ │ │ ├── Array+ApplicativeTest.swift │ │ │ ├── Array+FunctorTest.swift │ │ │ └── Array+MonadTest.swift │ │ ├── Const │ │ │ ├── Const+ApplicativeTest.swift │ │ │ ├── Const+Arbitrary.swift │ │ │ └── Const+FunctorTest.swift │ │ ├── Either │ │ │ ├── Either+ApplicativeErrorTest.swift │ │ │ ├── Either+ApplicativeTest.swift │ │ │ ├── Either+Arbitrary.swift │ │ │ ├── Either+FunctorTest.swift │ │ │ └── Either+MonadTest.swift │ │ ├── Eval │ │ │ ├── Eval+ApplicativeTest.swift │ │ │ ├── Eval+Arbitrary.swift │ │ │ ├── Eval+FunctorTest.swift │ │ │ └── Eval+MonadTest.swift │ │ ├── Function │ │ │ ├── Function+ApplicativeTest.swift │ │ │ ├── Function+Arbitrary.swift │ │ │ ├── Function+Equatable.swift │ │ │ ├── Function+FunctorTest.swift │ │ │ └── Function+MonadTest.swift │ │ ├── Id │ │ │ ├── Id+ApplicativeTest.swift │ │ │ ├── Id+Arbitrary.swift │ │ │ ├── Id+FunctorTest.swift │ │ │ └── Id+MonadTest.swift │ │ ├── Ior │ │ │ ├── Ior+ApplicativeTest.swift │ │ │ ├── Ior+Arbitrary.swift │ │ │ ├── Ior+FunctorTest.swift │ │ │ └── Ior+MonadTest.swift │ │ ├── NonEmptyArray │ │ │ ├── NonEmptyArray+ApplicativeTest.swift │ │ │ ├── NonEmptyArray+Arbitrary.swift │ │ │ ├── NonEmptyArray+FunctorTest.swift │ │ │ └── NonEmptyArray+MonadTest.swift │ │ ├── Optional │ │ │ ├── Optional+ApplicativeErrorTest.swift │ │ │ ├── Optional+ApplicativeTest.swift │ │ │ ├── Optional+FunctorTest.swift │ │ │ └── Optional+MonadTest.swift │ │ ├── Pair │ │ │ ├── Pair+ApplicativeTest.swift │ │ │ ├── Pair+Arbitrary.swift │ │ │ ├── Pair+FunctorTest.swift │ │ │ └── Pair+MonadTest.swift │ │ ├── Result │ │ │ ├── Result+ApplicativeErrorTest.swift │ │ │ ├── Result+ApplicativeTest.swift │ │ │ ├── Result+FunctorTest.swift │ │ │ └── Result+MonadTest.swift │ │ ├── State │ │ │ ├── State+ApplicativeTest.swift │ │ │ ├── State+Arbitrary.swift │ │ │ ├── State+Equatable.swift │ │ │ ├── State+FunctorTest.swift │ │ │ └── State+MonadTest.swift │ │ ├── Store │ │ │ ├── Store+ApplicativeTest.swift │ │ │ ├── Store+Arbitrary.swift │ │ │ ├── Store+Equatable.swift │ │ │ └── Store+FunctorTest.swift │ │ ├── Try │ │ │ ├── Try+ApplicativeErrorTest.swift │ │ │ ├── Try+ApplicativeTest.swift │ │ │ ├── Try+Arbitrary.swift │ │ │ ├── Try+Equatable.swift │ │ │ ├── Try+FunctorTest.swift │ │ │ └── Try+MonadTest.swift │ │ ├── Validated │ │ │ ├── Validated+ApplicativeErrorTest.swift │ │ │ ├── Validated+ApplicativeTest.swift │ │ │ ├── Validated+Arbitrary.swift │ │ │ └── Validated+FunctorTest.swift │ │ └── Writer │ │ │ ├── Writer+ApplicativeTest.swift │ │ │ ├── Writer+Arbitrary.swift │ │ │ ├── Writer+FunctorTest.swift │ │ │ └── Writer+MonadTest.swift │ ├── Instances │ │ ├── BoolInstancesTest.swift │ │ ├── NumberInstancesTest.swift │ │ └── StringInstancesTest.swift │ └── Syntax │ │ ├── BooleanFunctionsTest.swift │ │ ├── CompositionTest.swift │ │ ├── CurryTest.swift │ │ ├── MemoizationTest.swift │ │ ├── PartialApplicationTest.swift │ │ ├── PredefTest.swift │ │ └── ReverseTest.swift ├── BowLiteEffectsTests │ ├── EnvIO │ │ ├── EnvIO+ApplicativeErrorTest.swift │ │ ├── EnvIO+ApplicativeTest.swift │ │ ├── EnvIO+Arbitrary.swift │ │ ├── EnvIO+ConcurrentTest.swift │ │ ├── EnvIO+Equatable.swift │ │ ├── EnvIO+FunctorTest.swift │ │ └── EnvIO+MonadTest.swift │ └── IO │ │ ├── IO+ApplicativeErrorTest.swift │ │ ├── IO+ApplicativeTest.swift │ │ ├── IO+Arbitrary.swift │ │ ├── IO+ConcurrentTest.swift │ │ ├── IO+Equatable.swift │ │ ├── IO+FunctorTest.swift │ │ └── IO+MonadTest.swift └── BowLiteLaws │ ├── AnyError.swift │ ├── BindingOperatorOverload.swift │ ├── CustomStringConvertibleLaws.swift │ ├── EquatableLaws.swift │ ├── MonoidLaws.swift │ ├── PropertyOperatorOverload.swift │ ├── SemigroupLaws.swift │ └── SemiringLaws.swift └── assets └── bow-lite-banner.png /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Report malfunctioning of the library 4 | title: [Bug] 5 | labels: '' 6 | assignees: Maintainers 7 | 8 | --- 9 | 10 | ## Description 11 | 12 | *Explain the problem you are experiencing.* 13 | 14 | ## Expected outcome 15 | 16 | *Describe the result you should obtain.* 17 | 18 | ## Observed outcome 19 | 20 | *Describe the result you are obtaining instead.* 21 | 22 | ## Code to reproduce the Bug 23 | 24 | *Provide minimum code, together with imports, that we can execute to reproduce the bug.* 25 | 26 | ```swift 27 | // Your code here 28 | ``` 29 | 30 | ## Bow modules, version, platform 31 | 32 | *Indicate which modules you are using from Bow Lite and which versions. If you are not using public releases, provide branch and commit hash, but we suggest you to only use published releases. Indicate which platform you are targetting (iOS, macOS, tvOS, watchOS, playground)* 33 | 34 | ## Tooling 35 | 36 | - Xcode version: 37 | 38 | ## Other 39 | 40 | *Include any other details that can help us fix this bug efficiently.* 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request new functionality in Bow Lite 4 | title: [Request] 5 | labels: '' 6 | assignees: Maintainers 7 | 8 | --- 9 | 10 | ## Description 11 | 12 | *Describe your proposal for new functionality.* 13 | 14 | ## Sample usage 15 | 16 | *Provide sample code of how the new functionality should work.* 17 | 18 | ## Potential implementation 19 | 20 | *Provide an outline of how this functionality could be implemented.* 21 | 22 | ## Modules 23 | 24 | *List the modules that may be affected by this new functionality. Describe if any new modules will need to be created.* 25 | 26 | ## Breaking changes 27 | 28 | *Describe possible breaking changes that may happen if this functionality is included.* 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Related issues 2 | 3 | *Reference issues that you are addressing with this pull request.* 4 | 5 | ## Goal 6 | 7 | *Describe the objective of these changes.* 8 | 9 | ## Implementation details 10 | 11 | *Describe any remarkable decisions that you made in the implementation of these changes.* 12 | 13 | ## Testing details 14 | 15 | *Describe any remarkable decisions that you made in the tests of these changes. 16 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Compile and test 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | macos: 7 | name: macos 8 | runs-on: macos-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Switch Xcode version 13 | run: sudo xcode-select -s /Applications/Xcode_11.4.1.app/Contents/Developer 14 | - name: Run tests 15 | run: swift test 16 | - name: Generate linux tests 17 | run: swift test --generate-linuxmain 18 | - name: Cached auto-generate linux tests 19 | uses: actions/upload-artifact@v1 20 | with: 21 | name: generate-linuxmain 22 | path: Tests 23 | 24 | linux: 25 | name: linux 26 | needs: macos 27 | runs-on: ubuntu-latest 28 | 29 | steps: 30 | - uses: actions/checkout@v2 31 | - name: Clean Tests 32 | run: rm -rf Tests 33 | - name: Get auto-generate linux tests 34 | uses: actions/download-artifact@v1 35 | with: 36 | name: generate-linuxmain 37 | path: Tests 38 | - name: Remove generated artifact 39 | uses: geekyeggo/delete-artifact@v1 40 | with: 41 | name: generate-linuxmain 42 | failOnError: false 43 | - name: Run tests 44 | run: swift test --parallel --num-workers 4 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Swift Package Manager 2 | .DS_Store 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .build 7 | .swiftpm 8 | 9 | # Linux - generated swift files 10 | LinuxMain.swift 11 | **/XCTestManifests.swift 12 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "SwiftCheck", 6 | "repositoryURL": "https://github.com/bow-swift/SwiftCheck.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "748359f9a95edf94d0c4664102f104f56b1ff1fb", 10 | "version": "0.12.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Bow Lite](assets/bow-lite-banner.png) 2 | 3 |

4 | Compile and test 5 | 6 | iPad + iOS + macOS + linux 7 | bow-lite Playground 8 | 9 | 10 | Bow Lite is a cross-platform library for Typed Functional Programming in Swift. It is a lightweight version of [Bow](https://github.com/bow-swift/bow) where some complexity, like Higher Kinded Type emulation, has been removed. 11 | 12 | ## Documentation 13 | 14 | All documentation is available in [our website](https://bow-swift.io/). Notice that the documentation presents all examples using the full version of Bow. Although Bow Lite maintains compatibility with the API of Bow, there may be cases where there are some variations. 15 | 16 | ## Modules 17 | 18 | Bow Lite offers an umbrella module `BowLite` that contains: 19 | 20 | - **BowLiteCore**: a collection of the main data types typically used in FP projects, that you can find in the core module of Bow. 21 | - **BowLiteEffects**: an implementation of the `IO` and `EnvIO` data types to deal with side effects, that you can find in the BowEffects module. 22 | - **BowLiteOptics**: a monomorphic implementation of some optics, that you can find in the BowOptics module. 23 | 24 | ## How to get it 25 | 26 | Bow Lite is available using Swift Package Manager. You can include it using the corresponding wizard in Xcode, or adding the following line to your `Package.swift` manifest: 27 | 28 | ```swift 29 | .package(url: "https://github.com/bow-swift/bow-lite.git", from: "{version}") 30 | ``` 31 | 32 | ```swift 33 | import BowLite 34 | ``` 35 | 36 | # License 37 | 38 | Copyright (C) 2018-2021 The Bow Authors 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /Sources/BowLite/Headers.swift: -------------------------------------------------------------------------------- 1 | @_exported import BowLiteCore 2 | @_exported import BowLiteEffects 3 | @_exported import BowLiteOptics 4 | 5 | precedencegroup KleisliCompositionPrecedence { 6 | associativity: left 7 | } 8 | 9 | infix operator >=>: KleisliCompositionPrecedence 10 | 11 | precedencegroup CompositionPrecedence { 12 | associativity: left 13 | } 14 | 15 | infix operator <<<: CompositionPrecedence 16 | infix operator >>>: CompositionPrecedence 17 | 18 | precedencegroup PartialApplicationPrecedence {} 19 | 20 | infix operator |> : PartialApplicationPrecedence 21 | 22 | infix operator <- : AssignmentPrecedence 23 | prefix operator |<- 24 | 25 | // Using operator / to obtain prisms, as seen on 26 | // Pointfree's Case Paths: https://github.com/pointfreeco/swift-case-paths 27 | prefix operator / 28 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Array/Array+Functor.swift: -------------------------------------------------------------------------------- 1 | public extension Array { 2 | /// Given a function, provides a new function lifted to the context of this type. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context of this type. 6 | static func lift(_ f: @escaping (Element) -> B) -> (Array) -> Array { 7 | { array in array.map(f) } 8 | } 9 | 10 | /// Transforms the value type with a constant value. 11 | /// 12 | /// - Parameters: 13 | /// - b: Constant value to replace the value type. 14 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 15 | func `as`(_ b: B) -> Array { 16 | self.map(constant(b)) 17 | } 18 | 19 | /// Replaces the value type by the `Void` type. 20 | /// 21 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 22 | func void() -> Array { 23 | self.as(()) 24 | } 25 | 26 | /// Transforms the value type and pairs it with its original value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Transforming function. 30 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 31 | func product(_ f: @escaping (Element) -> B) -> Array<(Element, B)> { 32 | self.map { a in (a, f(a)) } 33 | } 34 | 35 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 36 | /// 37 | /// - Parameters: 38 | /// - b: Constant value for the tuple. 39 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 40 | func tupleLeft(_ b: B) -> Array<(B, Element)> { 41 | self.map { a in (b, a) } 42 | } 43 | 44 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 45 | /// 46 | /// - Parameters: 47 | /// - b: Constant value for the tuple. 48 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 49 | func tupleRight(_ b: B) -> Array<(Element, B)> { 50 | self.map { a in (a, b) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Array/Array+FunctorFilter.swift: -------------------------------------------------------------------------------- 1 | public extension Array { 2 | /// Maps the value/s in this context, filtering out the ones resulting in `nil`. 3 | /// 4 | /// - Parameters: 5 | /// - f: A function to map objects and filter them out. 6 | /// - Returns: Transformed and filtered values, in this context. 7 | func mapFilter(_ f: @escaping (Element) -> A?) -> [A] { 8 | self.compactMap(f) 9 | } 10 | 11 | /// Removes the `nil` value/s in this context and extracts the present ones. 12 | /// 13 | /// - Returns: Plain values in this context. 14 | func flattenOptional() -> [A] where Element == A? { 15 | mapFilter(id) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Array/Array+Monad.swift: -------------------------------------------------------------------------------- 1 | public extension Array { 2 | /// Flattens this nested structure into a single layer. 3 | /// 4 | /// - Returns: Value with a single context structure. 5 | func flatten() -> Array where Element == Array { 6 | self.flatMap(id) 7 | } 8 | 9 | /// Sequentially compose two computations, discarding the value produced by the first. 10 | /// 11 | /// - Parameters: 12 | /// - fb: 2nd computation. 13 | /// - Returns: Result of running the second computation after the first one. 14 | func followedBy(_ fb: Array) -> Array { 15 | self.flatMap(constant(fb)) 16 | } 17 | 18 | /// Sequentially compose two computations, discarding the value produced by the second. 19 | /// 20 | /// - Parameters: 21 | /// - fb: 2nd computation. 22 | /// - Returns: Result produced from the first computation after both are computed. 23 | func forEffect(_ fb: Array) -> Array { 24 | self.flatMap { wrapped in fb.as(wrapped) } 25 | } 26 | 27 | /// Pair the result of a computation with the result of applying a function to such result. 28 | /// 29 | /// - Parameters: 30 | /// - f: A function to be applied to the result of the computation. 31 | /// - Returns: A tuple of the result of the computation paired with the result of the function, in this context. 32 | func mproduct(_ f: @escaping (Element) -> Array) -> Array<(Element, A)> { 33 | self.flatMap { wrapped in 34 | f(wrapped).tupleLeft(wrapped) 35 | } 36 | } 37 | 38 | /// Conditionally apply a closure based on the boolean result of this computation. 39 | /// 40 | /// - Parameters: 41 | /// - then: Closure to be applied if the computation evaluates to `true`. 42 | /// - else: Closure to be applied if the computation evaluates to `false`. 43 | /// - Returns: Result of applying the corresponding closure based on the result of the computation. 44 | func `if`( 45 | then f: @escaping () -> Array, 46 | else g: @escaping () -> Array 47 | ) -> Array where Element == Bool { 48 | self.flatMap { boolean in 49 | boolean ? f() : g() 50 | } 51 | } 52 | 53 | /// Applies a monadic function and discard the result while keeping the effect. 54 | /// 55 | /// - Parameters: 56 | /// - f: A monadic function which result will be discarded. 57 | /// - Returns: A computation with the result of the initial computation and the effect caused by the function application. 58 | func flatTap(_ f: @escaping (Element) -> Array) -> Array { 59 | self.flatMap { wrapped in f(wrapped).as(wrapped) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Array/Array+Utilities.swift: -------------------------------------------------------------------------------- 1 | public extension Array { 2 | /// Accesses the element at a given index, safely. 3 | /// 4 | /// Index must be in the range [0, count) to be able to get or set the corresponding element in the array. 5 | /// 6 | /// - Parameter index: Integer number with the offset of the element in the array that wants to be accessed. 7 | /// - Returns: Optionally, the element located at that position in the array. 8 | subscript(safe index: Int) -> Element? { 9 | get { 10 | guard index >= 0, index < count else { 11 | return nil 12 | } 13 | return self[index] 14 | } 15 | 16 | set(newValue) { 17 | guard index >= 0, index < count, let value = newValue else { 18 | return 19 | } 20 | self[index] = value 21 | } 22 | } 23 | } 24 | 25 | // MARK: Instance of Semigroup for Array 26 | 27 | extension Array: Semigroup { 28 | public func combine(_ other: Array) -> Array { 29 | self + other 30 | } 31 | } 32 | 33 | // MARK: Instance of Monoid for Array 34 | 35 | extension Array: Monoid { 36 | public static var empty: Array { 37 | [] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Const/Const.swift: -------------------------------------------------------------------------------- 1 | /// Constant data type. Represents a container of two types, holding a value of the left type that remains constant, regardless of the transformation applied to it. 2 | public struct Const { 3 | public let value: Constant 4 | 5 | /// Initializes a constant value. 6 | /// 7 | /// - Parameter value: Constant value to be wrapped. 8 | public init(_ value: Constant) { 9 | self.value = value 10 | } 11 | 12 | /// Changes the type of the right type argument associated to this constant value. 13 | /// 14 | /// - Returns: The same wrapped value, changing the right type argument. 15 | public func retag() -> Const { 16 | Const(value) 17 | } 18 | } 19 | 20 | // MARK: Instance of Semigroup for Const 21 | 22 | extension Const: Semigroup where Constant: Semigroup { 23 | public func combine(_ other: Const) -> Const { 24 | Const(self.value.combine(other.value)) 25 | } 26 | } 27 | 28 | // MARK: Instance of Monoid for Const 29 | 30 | extension Const: Monoid where Constant: Monoid { 31 | public static var empty: Const { 32 | Const(.empty) 33 | } 34 | } 35 | 36 | // MARK: Conformance to CustomStringConvertible for Const 37 | 38 | extension Const: CustomStringConvertible where Constant: CustomStringConvertible { 39 | public var description: String { 40 | "Const(\(self.value.description))" 41 | } 42 | } 43 | 44 | // MARK: Conformance to Equatable for Const 45 | 46 | extension Const: Equatable where Constant: Equatable {} 47 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Either/Either+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | public extension Either { 2 | /// Lifts an error to this context. 3 | /// 4 | /// - Parameter error: A value of the error type. 5 | /// - Returns: A value representing the error in this context. 6 | static func raiseError(_ error: Left) -> Either { 7 | .left(error) 8 | } 9 | 10 | /// Handles an error, potentially recovering from it by mapping it to a value in this context. 11 | /// 12 | /// - Parameters: 13 | /// - f: A recovery function. 14 | /// - Returns: A value where the possible errors have been recovered using the provided function. 15 | func handleErrorWith(_ f: @escaping (Left) -> Either) -> Either { 16 | self.fold(f, Either.right) 17 | } 18 | 19 | /// Handles an error, potentially recovering from it by mapping it to a value. 20 | /// 21 | /// - Parameters: 22 | /// - f: A recovery function. 23 | /// - Returns: A value where the possible errors have been recovered using the provided function. 24 | func handleError(_ f: @escaping (Left) -> Right) -> Either { 25 | handleErrorWith(f >>> Either.right) 26 | } 27 | } 28 | 29 | public extension Either where Left: Error { 30 | /// Creates a value of this type from a Result. 31 | /// 32 | /// - Parameter result: Result value to convert to this type. 33 | /// - Returns: A value that represents the same content from Result, in this context. 34 | static func from(result: Result) -> Either { 35 | result.fold(Either.right, Either.left) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Either/Either+Functor.swift: -------------------------------------------------------------------------------- 1 | public extension Either { 2 | /// Given a function, provides a new function lifted to the context of this type. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context of this type. 6 | static func lift(_ f: @escaping (Right) -> B) -> (Either) -> Either { 7 | { either in either.map(f) } 8 | } 9 | 10 | /// Transforms the value type with a constant value. 11 | /// 12 | /// - Parameters: 13 | /// - b: Constant value to replace the value type. 14 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 15 | func `as`(_ b: B) -> Either { 16 | self.map(constant(b)) 17 | } 18 | 19 | /// Replaces the value type by the `Void` type. 20 | /// 21 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 22 | func void() -> Either { 23 | self.as(()) 24 | } 25 | 26 | /// Transforms the value type and pairs it with its original value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Transforming function. 30 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 31 | func product(_ f: @escaping (Right) -> B) -> Either { 32 | self.map { a in (a, f(a)) } 33 | } 34 | 35 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 36 | /// 37 | /// - Parameters: 38 | /// - b: Constant value for the tuple. 39 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 40 | func tupleLeft(_ b: B) -> Either { 41 | self.map { a in (b, a) } 42 | } 43 | 44 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 45 | /// 46 | /// - Parameters: 47 | /// - b: Constant value for the tuple. 48 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 49 | func tupleRight(_ b: B) -> Either { 50 | self.map { a in (a, b) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Either/Either+Monad.swift: -------------------------------------------------------------------------------- 1 | public extension Either { 2 | /// Flattens this nested structure into a single layer. 3 | /// 4 | /// - Returns: Value with a single context structure. 5 | func flatten() -> Either where Right == Either { 6 | self.flatMap(id) 7 | } 8 | 9 | /// Sequentially compose two computations, discarding the value produced by the first. 10 | /// 11 | /// - Parameters: 12 | /// - fa: 2nd computation. 13 | /// - Returns: Result of running the second computation after the first one. 14 | func followedBy(_ fa: Either) -> Either { 15 | self.flatMap(constant(fa)) 16 | } 17 | 18 | /// Sequentially compose two computations, discarding the value produced by the second. 19 | /// 20 | /// - Parameters: 21 | /// - fa: 2nd computation. 22 | /// - Returns: Result produced from the first computation after both are computed. 23 | func forEffect(_ fa: Either) -> Either { 24 | self.flatMap { wrapped in fa.as(wrapped) } 25 | } 26 | 27 | /// Pair the result of a computation with the result of applying a function to such result. 28 | /// 29 | /// - Parameters: 30 | /// - f: A function to be applied to the result of the computation. 31 | /// - Returns: A tuple of the result of the computation paired with the result of the function, in this context. 32 | func mproduct(_ f: @escaping (Right) -> Either) -> Either { 33 | self.flatMap { wrapped in 34 | f(wrapped).tupleLeft(wrapped) 35 | } 36 | } 37 | 38 | /// Conditionally apply a closure based on the boolean result of this computation. 39 | /// 40 | /// - Parameters: 41 | /// - then: Closure to be applied if the computation evaluates to `true`. 42 | /// - else: Closure to be applied if the computation evaluates to `false`. 43 | /// - Returns: Result of applying the corresponding closure based on the result of the computation. 44 | func `if`( 45 | then f: @escaping () -> Either, 46 | else g: @escaping () -> Either 47 | ) -> Either where Right == Bool { 48 | self.flatMap { boolean in 49 | boolean ? f() : g() 50 | } 51 | } 52 | 53 | /// Applies a monadic function and discard the result while keeping the effect. 54 | /// 55 | /// - Parameters: 56 | /// - f: A monadic function which result will be discarded. 57 | /// - Returns: A computation with the result of the initial computation and the effect caused by the function application. 58 | func flatTap(_ f: @escaping (Right) -> Either) -> Either { 59 | self.flatMap { wrapped in f(wrapped).as(wrapped) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Eval/Eval+Comonad.swift: -------------------------------------------------------------------------------- 1 | public extension Eval { 2 | /// Wraps a value in another layer of this context. 3 | /// 4 | /// - Returns: Value wrapped in another context layer. 5 | func duplicate() -> Eval> { 6 | coflatMap(id) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Eval/Eval+Functor.swift: -------------------------------------------------------------------------------- 1 | public extension Eval { 2 | /// Given a function, provides a new function lifted to the context of this type. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context of this type. 6 | static func lift(_ f: @escaping (Value) -> B) -> (Eval) -> Eval { 7 | { either in either.map(f) } 8 | } 9 | 10 | /// Transforms the value type with a constant value. 11 | /// 12 | /// - Parameters: 13 | /// - b: Constant value to replace the value type. 14 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 15 | func `as`(_ b: B) -> Eval { 16 | self.map(constant(b)) 17 | } 18 | 19 | /// Replaces the value type by the `Void` type. 20 | /// 21 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 22 | func void() -> Eval { 23 | self.as(()) 24 | } 25 | 26 | /// Transforms the value type and pairs it with its original value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Transforming function. 30 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 31 | func product(_ f: @escaping (Value) -> B) -> Eval<(Value, B)> { 32 | self.map { a in (a, f(a)) } 33 | } 34 | 35 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 36 | /// 37 | /// - Parameters: 38 | /// - b: Constant value for the tuple. 39 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 40 | func tupleLeft(_ b: B) -> Eval<(B, Value)> { 41 | self.map { a in (b, a) } 42 | } 43 | 44 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 45 | /// 46 | /// - Parameters: 47 | /// - b: Constant value for the tuple. 48 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 49 | func tupleRight(_ b: B) -> Eval<(Value, B)> { 50 | self.map { a in (a, b) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Eval/Eval+Monad.swift: -------------------------------------------------------------------------------- 1 | public extension Eval { 2 | /// Flattens this nested structure into a single layer. 3 | /// 4 | /// - Returns: Value with a single context structure. 5 | func flatten() -> Eval where Value == Eval { 6 | self.flatMap(id) 7 | } 8 | 9 | /// Sequentially compose two computations, discarding the value produced by the first. 10 | /// 11 | /// - Parameters: 12 | /// - fb: 2nd computation. 13 | /// - Returns: Result of running the second computation after the first one. 14 | func followedBy(_ fa: Eval) -> Eval { 15 | self.flatMap(constant(fa)) 16 | } 17 | 18 | /// Sequentially compose two computations, discarding the value produced by the second. 19 | /// 20 | /// - Parameters: 21 | /// - fb: 2nd computation. 22 | /// - Returns: Result produced from the first computation after both are computed. 23 | func forEffect(_ fa: Eval) -> Eval { 24 | self.flatMap { wrapped in fa.as(wrapped) } 25 | } 26 | 27 | /// Pair the result of a computation with the result of applying a function to such result. 28 | /// 29 | /// - Parameters: 30 | /// - f: A function to be applied to the result of the computation. 31 | /// - Returns: A tuple of the result of the computation paired with the result of the function, in this context. 32 | func mproduct(_ f: @escaping (Value) -> Eval) -> Eval<(Value, A)> { 33 | self.flatMap { wrapped in 34 | f(wrapped).tupleLeft(wrapped) 35 | } 36 | } 37 | 38 | /// Conditionally apply a closure based on the boolean result of this computation. 39 | /// 40 | /// - Parameters: 41 | /// - then: Closure to be applied if the computation evaluates to `true`. 42 | /// - else: Closure to be applied if the computation evaluates to `false`. 43 | /// - Returns: Result of applying the corresponding closure based on the result of the computation. 44 | func `if`( 45 | then f: @escaping () -> Eval, 46 | else g: @escaping () -> Eval 47 | ) -> Eval where Value == Bool { 48 | self.flatMap { boolean in 49 | boolean ? f() : g() 50 | } 51 | } 52 | 53 | /// Applies a monadic function and discard the result while keeping the effect. 54 | /// 55 | /// - Parameters: 56 | /// - f: A monadic function which result will be discarded. 57 | /// - Returns: A computation with the result of the initial computation and the effect caused by the function application. 58 | func flatTap(_ f: @escaping (Value) -> Eval) -> Eval { 59 | self.flatMap { wrapped in f(wrapped).as(wrapped) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Function/Function+Contravariant.swift: -------------------------------------------------------------------------------- 1 | public extension Function { 2 | /// Given a function, provides a new function lifted to this context type, but reversing the direction of the arrow. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context implementing this instance. 6 | static func contralift(_ f: @escaping (A) -> Input) -> (Function) -> Function { 7 | { function in function.contramap(f) } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Function/Function+Functor.swift: -------------------------------------------------------------------------------- 1 | public extension Function { 2 | /// Given a function, provides a new function lifted to the context of this type. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context of this type. 6 | static func lift(_ f: @escaping (Output) -> B) -> (Function) -> Function { 7 | { function in function.map(f) } 8 | } 9 | 10 | /// Transforms the value type with a constant value. 11 | /// 12 | /// - Parameters: 13 | /// - b: Constant value to replace the value type. 14 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 15 | func `as`(_ b: B) -> Function { 16 | self.map(constant(b)) 17 | } 18 | 19 | /// Replaces the value type by the `Void` type. 20 | /// 21 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 22 | func void() -> Function { 23 | self.as(()) 24 | } 25 | 26 | /// Transforms the value type and pairs it with its original value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Transforming function. 30 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 31 | func product(_ f: @escaping (Output) -> B) -> Function { 32 | self.map { a in (a, f(a)) } 33 | } 34 | 35 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 36 | /// 37 | /// - Parameters: 38 | /// - b: Constant value for the tuple. 39 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 40 | func tupleLeft(_ b: B) -> Function { 41 | self.map { a in (b, a) } 42 | } 43 | 44 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 45 | /// 46 | /// - Parameters: 47 | /// - b: Constant value for the tuple. 48 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 49 | func tupleRight(_ b: B) -> Function { 50 | self.map { a in (a, b) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Function/Function+MonadReader.swift: -------------------------------------------------------------------------------- 1 | public extension Function where Input == Output { 2 | /// Retrieves the shared environment. 3 | /// 4 | /// - Returns: Shared environment. 5 | static func ask() -> Function { 6 | Function(id) 7 | } 8 | } 9 | 10 | public extension Function { 11 | /// Executes this computation in a modified environment. 12 | /// 13 | /// - Parameters: 14 | /// - f: Funtion to modify the environment. 15 | /// - Returns: Computation in the modified environment. 16 | func local(_ f: @escaping (Input) -> Input) -> Function { 17 | self.contramap(f) 18 | } 19 | 20 | /// Retrieves a function of the current environment. 21 | /// 22 | /// - Parameter f: Selector function to apply to the environment. 23 | /// - Returns: A value extracted from the environment, in this context. 24 | static func reader(_ f: @escaping (Input) -> Output) -> Function { 25 | Function(f) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Function/Function.swift: -------------------------------------------------------------------------------- 1 | /// Type alias over Function. 2 | public typealias Reader = Function 3 | 4 | /// This data type acts as a wrapper over functions. It receives two type parameters representing the input and output type of the function. 5 | public struct Function { 6 | private let f: (Input) -> Output 7 | 8 | /// Constructs a value of `Function`. 9 | /// 10 | /// - Parameter f: Function to be wrapped in this type. 11 | public init(_ f: @escaping (Input) -> Output) { 12 | self.f = f 13 | } 14 | 15 | /// Invokes this function. 16 | /// 17 | /// - Parameter input: Input for the function. 18 | /// - Returns: Result of the invocation. 19 | public func callAsFunction(_ input: Input) -> Output { 20 | f(input) 21 | } 22 | 23 | /// Invokes this function. 24 | /// 25 | /// - Parameter input: Input for the function. 26 | /// - Returns: Result of the invocation. 27 | public func invoke(_ input: Input) -> Output { 28 | f(input) 29 | } 30 | 31 | /// Concatenates another function. 32 | /// 33 | /// - Parameter other: Function to concatenate. 34 | /// - Returns: Concatenation of the two functions. 35 | public func andThen(_ other: Function) -> Function { 36 | Function(self.f >>> other.f) 37 | } 38 | 39 | /// Pre- and post- composes this function with the provided arguments. 40 | /// 41 | /// - Parameters: 42 | /// - pre: Function to pre-compose with this function. 43 | /// - post: Function to post-compose with this function. 44 | /// - Returns: Result of composing the three functions. 45 | public func dimap( 46 | _ pre: @escaping (A) -> Input, 47 | _ post: @escaping (Output) -> B 48 | ) -> Function { 49 | Function(pre >>> self.f >>> post) 50 | } 51 | 52 | /// Creates a new value transforming this type using the provided function, preserving the structure of the original type. 53 | /// 54 | /// The implementation of this function must obey two laws: 55 | /// 56 | /// 1. Preserve identity: 57 | /// 58 | /// map(fa, id) == fa 59 | /// 60 | /// 2. Preserve composition: 61 | /// 62 | /// map(map(fa, f), g) == map(fa, compose(g, f)) 63 | /// 64 | /// - Parameters: 65 | /// - f: A transforming function. 66 | /// - Returns: The result of transforming the value type using the provided function, maintaining the structure of the original value. 67 | public func map(_ f: @escaping (Output) -> B) -> Function { 68 | dimap(id, f) 69 | } 70 | 71 | /// Composes with another function. 72 | /// 73 | /// - Parameter f: Function to compose. 74 | /// - Returns: Composition of the two functions. 75 | public func contramap(_ f: @escaping (A) -> Input) -> Function { 76 | dimap(f, id) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Id/Id.swift: -------------------------------------------------------------------------------- 1 | /// The identity data type represents a context of having no effect on the type it wraps. A instance of `Id` is isomorphic to an instance of `Wrapped`; it is just wrapped without any additional information. 2 | public struct Id { 3 | /// Wrapped value. 4 | public let value: Wrapped 5 | 6 | /// Constructs a value of `Id`. 7 | /// 8 | /// - Parameter value: Value to be wrapped in `Id`. 9 | public init(_ value: Wrapped) { 10 | self.value = value 11 | } 12 | } 13 | 14 | // MARK: Instance of Semigroup for Id 15 | extension Id: Semigroup where Wrapped: Semigroup { 16 | public func combine(_ other: Id) -> Id { 17 | Id(self.value.combine(other.value)) 18 | } 19 | } 20 | 21 | // MARK: Instance of Monoid for Id 22 | extension Id: Monoid where Wrapped: Monoid { 23 | public static var empty: Id { 24 | Id(.empty) 25 | } 26 | } 27 | 28 | // MARK: Conformance of Equatable for Id 29 | extension Id: Equatable where Wrapped: Equatable {} 30 | 31 | // MARK: Conformance of CustomStringConvertible for Id 32 | extension Id: CustomStringConvertible where Wrapped: CustomStringConvertible { 33 | public var description: String { 34 | "Id(\(value.description))" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/NonEmptyArray/NonEmptyArray+FunctorFilter.swift: -------------------------------------------------------------------------------- 1 | public extension NonEmptyArray { 2 | /// Maps the value/s in this context, filtering out the ones resulting in `nil`. 3 | /// 4 | /// - Parameters: 5 | /// - f: A function to map objects and filter them out. 6 | /// - Returns: Transformed and filtered values, in this context. 7 | func compactMap(_ f: @escaping (Element) -> A?) -> NEA { 8 | NEA(unsafe: self.asArray.compactMap(f)) 9 | } 10 | 11 | /// Maps the value/s in this context, filtering out the ones resulting in `nil`. 12 | /// 13 | /// - Parameters: 14 | /// - f: A function to map objects and filter them out. 15 | /// - Returns: Transformed and filtered values, in this context. 16 | func mapFilter(_ f: @escaping (Element) -> A?) -> NEA { 17 | self.compactMap(f) 18 | } 19 | 20 | /// Removes the `nil` value/s in this context and extracts the present ones. 21 | /// 22 | /// - Returns: Plain values in this context. 23 | func flattenOptional() -> NEA where Element == A? { 24 | mapFilter(id) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Optional/Optional+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | public extension Optional { 2 | /// Lifts an error to this context. 3 | /// 4 | /// - Returns: A value representing the error in this context. 5 | static func raiseError() -> Wrapped? { 6 | nil 7 | } 8 | 9 | /// Converts an Either value into this type. 10 | /// 11 | /// - Parameter either: Either value to be converted. 12 | /// - Returns: A value of this type containing the information from the Either value. 13 | static func from(either: Either) -> Wrapped? { 14 | either.fold(constant(nil), id) 15 | } 16 | 17 | /// Converts an Result value into this type. 18 | /// 19 | /// - Parameter result: Result value to be converted. 20 | /// - Returns: A value of this type containing the information from the Result value. 21 | static func from(result: Result) -> Wrapped? { 22 | result.fold(id, constant(nil)) 23 | } 24 | 25 | /// Handles an error, potentially recovering from it by mapping it to a value in this context. 26 | /// 27 | /// - Parameters: 28 | /// - f: A recovery function. 29 | /// - Returns: A value where the possible errors have been recovered using the provided function. 30 | func handleErrorWith(_ f: @escaping () -> Wrapped?) -> Wrapped? { 31 | self ?? f() 32 | } 33 | 34 | /// Handles an error, potentially recovering from it by mapping it to a value. 35 | /// 36 | /// - Parameters: 37 | /// - f: A recovery function. 38 | /// - Returns: A value where the possible errors have been recovered using the provided function. 39 | func handleError(_ f: @escaping () -> Wrapped) -> Wrapped? { 40 | handleErrorWith(f) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Optional/Optional+Functor.swift: -------------------------------------------------------------------------------- 1 | public extension Optional { 2 | /// Given a function, provides a new function lifted to the context of this type. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context of this type. 6 | static func lift(_ f: @escaping (Wrapped) -> B) -> (Wrapped?) -> B? { 7 | { optional in optional.map(f) } 8 | } 9 | 10 | /// Transforms the value type with a constant value. 11 | /// 12 | /// - Parameters: 13 | /// - b: Constant value to replace the value type. 14 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 15 | func `as`(_ b: B) -> B? { 16 | self.map(constant(b)) 17 | } 18 | 19 | /// Replaces the value type by the `Void` type. 20 | /// 21 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 22 | func void() -> Void? { 23 | self.as(()) 24 | } 25 | 26 | /// Transforms the value type and pairs it with its original value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Transforming function. 30 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 31 | func product(_ f: @escaping (Wrapped) -> B) -> (Wrapped, B)? { 32 | self.map { a in (a, f(a)) } 33 | } 34 | 35 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 36 | /// 37 | /// - Parameters: 38 | /// - b: Constant value for the tuple. 39 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 40 | func tupleLeft(_ b: B) -> (B, Wrapped)? { 41 | self.map { a in (b, a) } 42 | } 43 | 44 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 45 | /// 46 | /// - Parameters: 47 | /// - b: Constant value for the tuple. 48 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 49 | func tupleRight(_ b: B) -> (Wrapped, B)? { 50 | self.map { a in (a, b) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Optional/Optional+FunctorFilter.swift: -------------------------------------------------------------------------------- 1 | public extension Optional { 2 | /// Maps the value/s in this context, filtering out the ones resulting in `nil`. 3 | /// 4 | /// - Parameters: 5 | /// - f: A function to map objects and filter them out. 6 | /// - Returns: Transformed and filtered values, in this context. 7 | func mapFilter(_ f: @escaping (Wrapped) -> A?) -> A? { 8 | self.flatMap(f) 9 | } 10 | 11 | /// Filters out values that do not match a predicate. 12 | /// 13 | /// - Parameter f: Filtering predicate. 14 | /// - Returns: An optional value that contains the original value if it matched the predicate, or nil, otherwise. 15 | func filter(_ f: @escaping (Wrapped) -> Bool) -> Wrapped? { 16 | mapFilter { wrapped in 17 | f(wrapped) ? wrapped : nil 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Optional/Optional+Monad.swift: -------------------------------------------------------------------------------- 1 | public extension Optional { 2 | /// Flattens this nested structure into a single layer. 3 | /// 4 | /// - Returns: Value with a single context structure. 5 | func flatten() -> A? where Wrapped == A? { 6 | self.flatMap(id) 7 | } 8 | 9 | /// Sequentially compose two computations, discarding the value produced by the first. 10 | /// 11 | /// - Parameters: 12 | /// - fb: 2nd computation. 13 | /// - Returns: Result of running the second computation after the first one. 14 | func followedBy(_ fa: A?) -> A? { 15 | self.flatMap(constant(fa)) 16 | } 17 | 18 | /// Sequentially compose two computations, discarding the value produced by the second. 19 | /// 20 | /// - Parameters: 21 | /// - fb: 2nd computation. 22 | /// - Returns: Result produced from the first computation after both are computed. 23 | func forEffect(_ fa: A?) -> Wrapped? { 24 | self.flatMap { wrapped in fa.as(wrapped) } 25 | } 26 | 27 | /// Pair the result of a computation with the result of applying a function to such result. 28 | /// 29 | /// - Parameters: 30 | /// - f: A function to be applied to the result of the computation. 31 | /// - Returns: A tuple of the result of the computation paired with the result of the function, in this context. 32 | func mproduct(_ f: @escaping (Wrapped) -> A?) -> (Wrapped, A)? { 33 | self.flatMap { wrapped in 34 | f(wrapped).tupleLeft(wrapped) 35 | } 36 | } 37 | 38 | /// Conditionally apply a closure based on the boolean result of this computation. 39 | /// 40 | /// - Parameters: 41 | /// - then: Closure to be applied if the computation evaluates to `true`. 42 | /// - else: Closure to be applied if the computation evaluates to `false`. 43 | /// - Returns: Result of applying the corresponding closure based on the result of the computation. 44 | func `if`( 45 | then f: @escaping () -> A?, 46 | else g: @escaping () -> A? 47 | ) -> A? where Wrapped == Bool { 48 | self.flatMap { boolean in 49 | boolean ? f() : g() 50 | } 51 | } 52 | 53 | /// Applies a monadic function and discard the result while keeping the effect. 54 | /// 55 | /// - Parameters: 56 | /// - f: A monadic function which result will be discarded. 57 | /// - Returns: A computation with the result of the initial computation and the effect caused by the function application. 58 | func flatTap(_ f: @escaping (Wrapped) -> A?) -> Wrapped? { 59 | self.flatMap { wrapped in f(wrapped).as(wrapped) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Optional/Optional+Utilities.swift: -------------------------------------------------------------------------------- 1 | public extension Optional { 2 | /// Applies a function based on the presence or absence of a value. 3 | /// 4 | /// - Parameters: 5 | /// - ifNone: A closure that is executed when there is no value in the `Optional`. 6 | /// - ifSome: A closure that is executed where there is a value in the `Optional`. In such case, the the inner value is sent as an argument of this function. 7 | /// - Returns: Result of applying the corresponding closure based on the value of this object. 8 | func fold( 9 | _ ifNone: @escaping () -> A, 10 | _ ifSome: @escaping (Wrapped) -> A 11 | ) -> A { 12 | if let wrapped = self { 13 | return ifSome(wrapped) 14 | } else { 15 | return ifNone() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Pair/Pair.swift: -------------------------------------------------------------------------------- 1 | /// Pair describes a tuple of two elements. It is a nominal type over Swift tuples, which do not allow extensions to be made on them. 2 | public struct Pair { 3 | /// First component of the tuple. 4 | public let first: First 5 | /// Second component of the tuple. 6 | public let second: Second 7 | 8 | /// Initializes a pair. 9 | /// 10 | /// - Parameters: 11 | /// - first: First component of the tuple. 12 | /// - second: Second component of the tuple. 13 | public init(_ first: First, _ second: Second) { 14 | self.first = first 15 | self.second = second 16 | } 17 | 18 | /// Initializes a pair. 19 | /// 20 | /// - Parameter tuple: Tuple of two values. 21 | public init(_ tuple: (First, Second)) { 22 | self.first = tuple.0 23 | self.second = tuple.1 24 | } 25 | 26 | /// Transforms both type parameters, preserving the structure of this value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Closure to be applied on the first component. 30 | /// - g: Closure to be applied on the second component. 31 | /// - Returns: Result of applying the corresponding closure to this value. 32 | public func bimap( 33 | _ f: @escaping (First) -> A, 34 | _ g: @escaping (Second) -> B 35 | ) -> Pair { 36 | Pair(f(self.first), g(self.second)) 37 | } 38 | 39 | /// Transforms the first type parameter, preserving the structure of this value. 40 | /// 41 | /// - Parameter f: Transforming closure. 42 | /// - Returns: Result of appliying the transformation to the first component in this value. 43 | public func mapFirst( 44 | _ f: @escaping (First) -> L 45 | ) -> Pair { 46 | bimap(f, id) 47 | } 48 | } 49 | 50 | // MARK: Conformance to Equatable for Pair 51 | 52 | extension Pair: Equatable where First: Equatable, Second: Equatable {} 53 | 54 | // MARK: Conformance to CustomStringConvertible for Pair 55 | 56 | extension Pair: CustomStringConvertible where First: CustomStringConvertible, Second: CustomStringConvertible { 57 | public var description: String { 58 | "(\(self.first.description), \(self.second.description))" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Result/Result+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | public extension Result { 2 | /// Lifts an error to this context. 3 | /// 4 | /// - Parameter error: A value of the error type. 5 | /// - Returns: A value representing the error in this context. 6 | static func raiseError(_ error: Failure) -> Result { 7 | .failure(error) 8 | } 9 | 10 | /// Creates a value of this type from an Either. 11 | /// 12 | /// - Parameter either: Either value to convert to this type. 13 | /// - Returns: A value that represents the same content from Either, in this context. 14 | static func from(either: Either) -> Result { 15 | either.fold(Result.failure, Result.success) 16 | } 17 | 18 | /// Handles an error, potentially recovering from it by mapping it to a value in this context. 19 | /// 20 | /// - Parameters: 21 | /// - f: A recovery function. 22 | /// - Returns: A value where the possible errors have been recovered using the provided function. 23 | func handleErrorWith(_ f: @escaping (Failure) -> Result) -> Result { 24 | self.fold(Result.success, f) 25 | } 26 | 27 | /// Handles an error, potentially recovering from it by mapping it to a value. 28 | /// 29 | /// - Parameters: 30 | /// - f: A recovery function. 31 | /// - Returns: A value where the possible errors have been recovered using the provided function. 32 | func handleError(_ f: @escaping (Failure) -> Success) -> Result { 33 | handleErrorWith(f >>> Result.success) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Result/Result+Functor.swift: -------------------------------------------------------------------------------- 1 | public extension Result { 2 | /// Given a function, provides a new function lifted to the context of this type. 3 | /// 4 | /// - Parameter f: Function to be lifted. 5 | /// - Returns: Function in the context of this type. 6 | static func lift(_ f: @escaping (Success) -> B) -> (Result) -> Result { 7 | { result in result.map(f) } 8 | } 9 | 10 | /// Transforms the value type with a constant value. 11 | /// 12 | /// - Parameters: 13 | /// - b: Constant value to replace the value type. 14 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 15 | func `as`(_ b: B) -> Result { 16 | self.map(constant(b)) 17 | } 18 | 19 | /// Replaces the value type by the `Void` type. 20 | /// 21 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 22 | func void() -> Result { 23 | self.as(()) 24 | } 25 | 26 | /// Transforms the value type and pairs it with its original value. 27 | /// 28 | /// - Parameters: 29 | /// - f: Transforming function. 30 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 31 | func product(_ f: @escaping (Success) -> B) -> Result<(Success, B), Failure> { 32 | self.map { a in (a, f(a)) } 33 | } 34 | 35 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 36 | /// 37 | /// - Parameters: 38 | /// - b: Constant value for the tuple. 39 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 40 | func tupleLeft(_ b: B) -> Result<(B, Success), Failure> { 41 | self.map { a in (b, a) } 42 | } 43 | 44 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 45 | /// 46 | /// - Parameters: 47 | /// - b: Constant value for the tuple. 48 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 49 | func tupleRight(_ b: B) -> Result<(Success, B), Failure> { 50 | self.map { a in (a, b) } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Result/Result+Monad.swift: -------------------------------------------------------------------------------- 1 | public extension Result { 2 | /// Flattens this nested structure into a single layer. 3 | /// 4 | /// - Returns: Value with a single context structure. 5 | func flatten() -> Result where Success == Result { 6 | self.flatMap(id) 7 | } 8 | 9 | /// Sequentially compose two computations, discarding the value produced by the first. 10 | /// 11 | /// - Parameters: 12 | /// - fa: 2nd computation. 13 | /// - Returns: Result of running the second computation after the first one. 14 | func followedBy(_ fa: Result) -> Result { 15 | self.flatMap(constant(fa)) 16 | } 17 | 18 | /// Sequentially compose two computations, discarding the value produced by the second. 19 | /// 20 | /// - Parameters: 21 | /// - fa: 2nd computation. 22 | /// - Returns: Result produced from the first computation after both are computed. 23 | func forEffect(_ fa: Result) -> Result { 24 | self.flatMap { wrapped in fa.as(wrapped) } 25 | } 26 | 27 | /// Pair the result of a computation with the result of applying a function to such result. 28 | /// 29 | /// - Parameters: 30 | /// - f: A function to be applied to the result of the computation. 31 | /// - Returns: A tuple of the result of the computation paired with the result of the function, in this context. 32 | func mproduct(_ f: @escaping (Success) -> Result) -> Result<(Success, A), Failure> { 33 | self.flatMap { wrapped in 34 | f(wrapped).tupleLeft(wrapped) 35 | } 36 | } 37 | 38 | /// Conditionally apply a closure based on the boolean result of this computation. 39 | /// 40 | /// - Parameters: 41 | /// - then: Closure to be applied if the computation evaluates to `true`. 42 | /// - else: Closure to be applied if the computation evaluates to `false`. 43 | /// - Returns: Result of applying the corresponding closure based on the result of the computation. 44 | func `if`( 45 | then f: @escaping () -> Result, 46 | else g: @escaping () -> Result 47 | ) -> Result where Success == Bool { 48 | self.flatMap { boolean in 49 | boolean ? f() : g() 50 | } 51 | } 52 | 53 | /// Applies a monadic function and discard the result while keeping the effect. 54 | /// 55 | /// - Parameters: 56 | /// - f: A monadic function which result will be discarded. 57 | /// - Returns: A computation with the result of the initial computation and the effect caused by the function application. 58 | func flatTap(_ f: @escaping (Success) -> Result) -> Result { 59 | self.flatMap { wrapped in f(wrapped).as(wrapped) } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Result/Result+Utilities.swift: -------------------------------------------------------------------------------- 1 | public extension Result { 2 | /// Applies the provided closures based on the content of this `Result` value. 3 | /// 4 | /// - Parameters: 5 | /// - ifSuccess: Closure to apply if the contained value in this `Result` is a member of the Success type. 6 | /// - ifFailure: Closure to apply if the contained value in this `Result` is a member of the Failure type. 7 | /// - Returns: Result of applying the corresponding closure to this value. 8 | func fold( 9 | _ ifSuccess: @escaping (Success) -> A, 10 | _ ifFailure: @escaping (Failure) -> A 11 | ) -> A { 12 | switch self { 13 | case .failure(let error): return ifFailure(error) 14 | case .success(let success): return ifSuccess(success) 15 | } 16 | } 17 | 18 | /// Converts this value to an Either. 19 | /// 20 | /// - Returns: An Either value that maps failures to the left case, and successes to the right case. 21 | func toEither() -> Either { 22 | fold(Either.right, Either.left) 23 | } 24 | 25 | /// Converts this value to a Validated. 26 | /// 27 | /// - Returns: A Validated value that maps failures to the invalid case, and successes to the valid case. 28 | func toValidated() -> Validated { 29 | fold(Validated.valid, Validated.invalid) 30 | } 31 | 32 | /// Converts this value to a ValidatedNEA. 33 | /// 34 | /// - Returns: A ValidatedNEA value that maps failures to the invalid case, and successes to the valid case. 35 | func toValidatedNEA() -> ValidatedNEA { 36 | toValidated().toValidatedNEA() 37 | } 38 | 39 | /// Converts this value to a Try. 40 | /// 41 | /// - Returns: A Try value that maps failures to the failure case, and successes to the success case. 42 | func toTry() -> Try { 43 | fold(Try.success, Try.failure) 44 | } 45 | } 46 | 47 | // MARK: Instance of Semigroup for Result 48 | extension Result: Semigroup where Failure: Semigroup, Success: Semigroup { 49 | public func combine(_ other: Result) -> Result { 50 | switch (self, other) { 51 | case (.failure(let f1), .failure(let f2)): 52 | return .failure(f1.combine(f2)) 53 | case (.success(let s1), .success(let s2)): 54 | return .success(s1.combine(s2)) 55 | case (.failure(let f), _), (_, .failure(let f)): 56 | return .failure(f) 57 | } 58 | } 59 | } 60 | 61 | // MARK: Instance of Monoid for Result 62 | extension Result: Monoid where Failure: Monoid, Success: Monoid { 63 | public static var empty: Result { 64 | .success(.empty) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/State/State+MonadState.swift: -------------------------------------------------------------------------------- 1 | public extension State where StateType == Value { 2 | /// Retrieves the state from the internals of the monad. 3 | /// 4 | /// - Returns: Maintained state. 5 | static func get() -> State { 6 | State { state in (state, state) } 7 | } 8 | } 9 | 10 | public extension State where Value == Void { 11 | /// Replaces the state inside the monad. 12 | /// 13 | /// - Parameter s: New state. 14 | /// - Returns: Unit value with the new state. 15 | static func set(_ newState: StateType) -> State { 16 | State { _ in (newState, ()) } 17 | } 18 | 19 | /// Modifies the internal state. 20 | /// 21 | /// - Parameter f: Function that modifies the state. 22 | /// - Returns: Unit value with the modified state. 23 | static func modify(_ f: @escaping (StateType) -> StateType) -> State { 24 | State { state in (f(state), ()) } 25 | } 26 | } 27 | 28 | public extension State { 29 | /// Embeds a state action into this monad. 30 | /// 31 | /// - Parameter f: A function that receives the state and computes a value and a new state. 32 | /// - Returns: A value with the output of the function and the new state. 33 | static func state(_ f: @escaping (StateType) -> (StateType, Value)) -> State { 34 | State(f) 35 | } 36 | 37 | /// Retrieves a specific component of the state. 38 | /// 39 | /// - Parameter f: Projection function to obtain part of the state. 40 | /// - Returns: A specific part of the state. 41 | static func inspect(_ f: @escaping (StateType) -> Value) -> State { 42 | State.get().map(f) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/State/State.swift: -------------------------------------------------------------------------------- 1 | /// State represents operations that need to write and read a state through a computation. 2 | /// 3 | /// Some computations may not require the full power of this type: 4 | /// - For read-only state, see `Reader` / `Function`. 5 | /// - To accumulate a value without using it on the way, see `Writer`. 6 | public struct State { 7 | private let f: (StateType) -> (StateType, Value) 8 | 9 | /// Initializes a `StateT`. 10 | /// 11 | /// - Parameter f: A function that receives a state and produces an updates to such state and a value. 12 | public init(_ f: @escaping (StateType) -> (StateType, Value)) { 13 | self.f = f 14 | } 15 | 16 | /// Invokes this value with an initial state. 17 | /// 18 | /// - Parameter initialState: Initial state to be fed in the state-based function. 19 | /// - Returns: A tuple with the final state and result. 20 | public func callAsFunction(_ initialState: StateType) -> (StateType, Value) { 21 | f(initialState) 22 | } 23 | 24 | /// Invokes this value with an initial state. 25 | /// 26 | /// - Parameter initialState: Initial state to be fed in the state-based function. 27 | /// - Returns: A tuple with the final state and result. 28 | public func invoke(_ initialState: StateType) -> (StateType, Value) { 29 | f(initialState) 30 | } 31 | 32 | /// Invokes this value with an initial state. 33 | /// 34 | /// - Parameter initialState: Initial state to be fed in the state-based function. 35 | /// - Returns: Final state resulting from the evaluation of the computation. 36 | public func runState(_ initialState: StateType) -> StateType { 37 | self.invoke(initialState).0 38 | } 39 | 40 | /// Invokes this value with an initial state. 41 | /// 42 | /// - Parameter initialState: Initial state to be fed in the state-based function. 43 | /// - Returns: Final value resulting from the evaluation of the computation. 44 | public func runValue(_ initialState: StateType) -> Value { 45 | self.invoke(initialState).1 46 | } 47 | 48 | /// Modifies the state type of this computation using to functions to get and set part of this state from a larger state. 49 | /// 50 | /// - Parameters: 51 | /// - getter: Getter function to obtain part of the parent state. 52 | /// - setter: Setter function to set a new value in the parent state. 53 | /// - Returns: A new state focused on a new state type. 54 | public func focus( 55 | _ getter: @escaping (S) -> StateType, 56 | _ setter: @escaping (S, StateType) -> S 57 | ) -> State { 58 | State { state in 59 | let (newState, result) = self.invoke(getter(state)) 60 | return (setter(state, newState), result) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Store/Store+Comonad.swift: -------------------------------------------------------------------------------- 1 | public extension Store { 2 | /// Extracts the focused value of this context. 3 | /// 4 | /// - Returns: Focused value in this context. 5 | func extract() -> View { 6 | self.render(self.state) 7 | } 8 | 9 | /// Applies a context-dependent function to this structure. 10 | /// 11 | /// - Parameters: 12 | /// - f: Context-dependent function. 13 | /// - Returns: The result of applying the context-dependent function over the entire structure of this value. 14 | func coflatMap(_ f: @escaping (Store) -> B) -> Store { 15 | Store(state: self.state) { state in 16 | f(Store(state: state, render: self.render)) 17 | } 18 | } 19 | 20 | /// Wraps a value in another layer of this context. 21 | /// 22 | /// - Returns: Value wrapped in another context layer. 23 | func duplicate() -> Store> { 24 | coflatMap(id) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Store/Store+ComonadStore.swift: -------------------------------------------------------------------------------- 1 | public extension Store { 2 | /// Obtains the current position of the store. 3 | /// 4 | /// - Returns: Current position. 5 | var position: State { 6 | self.state 7 | } 8 | 9 | /// Obtains the value stored in the provided position. 10 | /// 11 | /// - Parameters: 12 | /// - s: Position within the Store. 13 | /// - Returns: Value stored in the provided position. 14 | func peek(_ state: State) -> View { 15 | self.render(state) 16 | } 17 | 18 | /// Obtains a value in a position relative to the current position. 19 | /// 20 | /// - Parameters: 21 | /// - f: Function to compute the relative position. 22 | /// - Returns: Value located in a relative position to the current one. 23 | func peeks(_ f: @escaping (State) -> State) -> View { 24 | peek(f(self.state)) 25 | } 26 | 27 | /// Moves to a new position. 28 | /// 29 | /// - Parameters: 30 | /// - s: New position. 31 | /// - Returns: Store focused into the new position. 32 | func seek(_ state: State) -> Store { 33 | coflatMap { store in store.peek(state) } 34 | } 35 | 36 | /// Moves to a new position relative to the current one. 37 | /// 38 | /// - Parameters: 39 | /// - f: Function to compute the new position, relative to the current one. 40 | /// - Returns: Store focused into the new position. 41 | func seeks(_ f: @escaping (State) -> State) -> Store { 42 | coflatMap { store in store.peeks(f) } 43 | } 44 | 45 | /// Moves the store into a new state. 46 | /// 47 | /// - Parameter newState: New state for the store. 48 | /// - Returns: A new store focused on the provided state. 49 | func move(_ newState: State) -> Store { 50 | duplicate().peek(newState) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Store/Store.swift: -------------------------------------------------------------------------------- 1 | /// Store represents values of type `View` that depend on a certain position of type `State`. 2 | public struct Store { 3 | /// Current focus of this store. 4 | public let state: State 5 | 6 | /// Function that yields values that depend on the focus type. 7 | public let render: (State) -> View 8 | 9 | /// Initializes a store. 10 | /// 11 | /// - Parameters: 12 | /// - state: Initial focus. 13 | /// - render: Rendering function. 14 | public init(state: State, render: @escaping (State) -> View) { 15 | self.state = state 16 | self.render = render 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Try/Try+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | public extension Try { 2 | /// Lifts an error to this context. 3 | /// 4 | /// - Parameter error: A value of the error type. 5 | /// - Returns: A value representing the error in this context. 6 | static func raiseError(_ error: Error) -> Try { 7 | .failure(error) 8 | } 9 | 10 | /// Creates a value of this type from a Result. 11 | /// 12 | /// - Parameter result: Result value to convert to this type. 13 | /// - Returns: A value that represents the same content from Result, in this context. 14 | static func from(result: Result) -> Try { 15 | result.fold(Try.success, Try.failure) 16 | } 17 | 18 | /// Creates a value of this type from an Either. 19 | /// 20 | /// - Parameter either: Result value to convert to this type. 21 | /// - Returns: A value that represents the same content from Either, in this context. 22 | static func from(either: Either) -> Try { 23 | either.fold(Try.failure, Try.success) 24 | } 25 | 26 | /// Handles an error, potentially recovering from it by mapping it to a value in this context. 27 | /// 28 | /// - Parameters: 29 | /// - f: A recovery function. 30 | /// - Returns: A value where the possible errors have been recovered using the provided function. 31 | func handleErrorWith(_ f: @escaping (Error) -> Try) -> Try { 32 | self.fold(f, Try.success) 33 | } 34 | 35 | /// Handles an error, potentially recovering from it by mapping it to a value. 36 | /// 37 | /// - Parameters: 38 | /// - f: A recovery function. 39 | /// - Returns: A value where the possible errors have been recovered using the provided function. 40 | func handleError(_ f: @escaping (Error) -> Success) -> Try { 41 | handleErrorWith(f >>> Try.success) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Validated/Validated+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | public extension Validated { 2 | /// Lifts an error to this context. 3 | /// 4 | /// - Parameter invalid: A value of the error type. 5 | /// - Returns: A value representing the error in this context. 6 | static func raiseError(_ invalid: Invalid) -> Validated { 7 | .invalid(invalid) 8 | } 9 | 10 | /// Creates a value of this type from an Either. 11 | /// 12 | /// - Parameter either: Result value to convert to this type. 13 | /// - Returns: A value that represents the same content from Either, in this context. 14 | static func from(either: Either) -> Validated { 15 | either.fold(Validated.invalid, Validated.valid) 16 | } 17 | 18 | /// Handles an error, potentially recovering from it by mapping it to a value in this context. 19 | /// 20 | /// - Parameters: 21 | /// - f: A recovery function. 22 | /// - Returns: A value where the possible errors have been recovered using the provided function. 23 | func handleErrorWith(_ f: @escaping (Invalid) -> Validated) -> Validated { 24 | self.fold(f, Validated.valid) 25 | } 26 | 27 | /// Handles an error, potentially recovering from it by mapping it to a value. 28 | /// 29 | /// - Parameters: 30 | /// - f: A recovery function. 31 | /// - Returns: A value where the possible errors have been recovered using the provided function. 32 | func handleError(_ f: @escaping (Invalid) -> Valid) -> Validated { 33 | handleErrorWith(f >>> Validated.valid) 34 | } 35 | } 36 | 37 | public extension Validated where Invalid: Error { 38 | /// Creates a value of this type from a Result. 39 | /// 40 | /// - Parameter result: Result value to convert to this type. 41 | /// - Returns: A value that represents the same content from Result, in this context. 42 | static func from(result: Result) -> Validated { 43 | result.fold(Validated.valid, Validated.invalid) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Writer/Writer+MonadWriter.swift: -------------------------------------------------------------------------------- 1 | public extension Writer where Accumulator: Monoid { 2 | /// Embeds a writer action. 3 | /// 4 | /// - Parameter pair: A tupe of the writer type and a value. 5 | /// - Returns: The writer action embedded in this context. 6 | static func writer(_ pair: (Accumulator, Value)) -> Writer { 7 | Writer(pair.0, pair.1) 8 | } 9 | 10 | /// Adds the side stream of data to the result of a computation. 11 | /// 12 | /// - Returns: The result of the computation paired with the side stream of data. 13 | func listen() -> Writer { 14 | Writer( 15 | self.accumulator, 16 | (self.accumulator, self.value) 17 | ) 18 | } 19 | 20 | /// Performs a computation and transforms the side stream of data, pairing it with the result of the computation. 21 | /// 22 | /// - Parameters: 23 | /// - f: A function to transform the side stream of data. 24 | /// - Returns: A tuple of the transformation of the side stream and the result of the computation. 25 | func listens(_ f: @escaping (Accumulator) -> Accumulator) -> Writer { 26 | self.listen().map { pair in (f(pair.0), pair.1) } 27 | } 28 | 29 | /// Performs a computation and transforms the side stream of data. 30 | /// 31 | /// - Returns: Result of the computation. 32 | func pass() -> Writer where Value == ((Accumulator) -> Accumulator, A) { 33 | Writer(self.value.0(self.accumulator), self.value.1) 34 | } 35 | 36 | /// Transforms the side stream of data of a given computation. 37 | /// 38 | /// - Parameters: 39 | /// - f: Transforming function. 40 | /// - Returns: A computation with the same result as the provided one, with the transformed side stream of data. 41 | func censor(_ f: @escaping (Accumulator) -> Accumulator) -> Writer { 42 | self.map { value in (f, value) }.pass() 43 | } 44 | } 45 | 46 | public extension Writer where Accumulator: Monoid, Value == Void { 47 | /// Produces a new value of the side stream of data. 48 | /// 49 | /// - Parameter w: New value. 50 | /// - Returns: Unit. 51 | static func tell(_ accumulator: Accumulator) -> Writer { 52 | Writer(accumulator, ()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Data/Writer/Writer.swift: -------------------------------------------------------------------------------- 1 | /// Writer represents operations that accumulate values through a computation, without reading them. 2 | public struct Writer { 3 | /// Side stream of accumulated data through computations. 4 | public let accumulator: Accumulator 5 | 6 | /// Value of the computation. 7 | public let value: Value 8 | 9 | /// Initializes a Writer. 10 | /// 11 | /// - Parameters: 12 | /// - accumulator: Initial value for the side strem of accumulated data. 13 | /// - value: Initial value for the computation. 14 | public init(_ accumulator: Accumulator, _ value: Value) { 15 | self.accumulator = accumulator 16 | self.value = value 17 | } 18 | } 19 | 20 | // MARK: Conformance to Equatable for Writer 21 | extension Writer: Equatable where Accumulator: Equatable, Value: Equatable {} 22 | 23 | // MARK: Conformance to CustomStringConvertible for Writer 24 | extension Writer: CustomStringConvertible where Accumulator: CustomStringConvertible, Value: CustomStringConvertible { 25 | public var description: String { 26 | "Writer(\(accumulator.description), \(value.description))" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Instances/BoolInstances.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // MARK: Instance of Semigroup for Bool. Uses disjunction as combination of elements. 4 | extension Bool: Semigroup { 5 | public func combine(_ other: Bool) -> Bool { 6 | self || other 7 | } 8 | } 9 | 10 | // MARK: Instance of Monoid for Bool. Uses false as empty element. 11 | extension Bool: Monoid { 12 | public static var empty: Bool { 13 | false 14 | } 15 | } 16 | 17 | // MARK: Instance of Semiring for Bool. Uses conjunction as multiplication of elements and true as unit element. 18 | extension Bool: Semiring { 19 | public func multiply(_ other: Bool) -> Bool { 20 | self && other 21 | } 22 | 23 | public static var one: Bool { 24 | true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Instances/StringInstances.swift: -------------------------------------------------------------------------------- 1 | // MARK: Instance of Semigroup for String, using concatenation as combination method 2 | extension String: Semigroup { 3 | public func combine(_ other: String) -> String { 4 | self + other 5 | } 6 | } 7 | 8 | // MARK: Instance of Monoid for String, using empty string as empty element 9 | extension String: Monoid { 10 | public static var empty: String { 11 | "" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Syntax/Memoization.swift: -------------------------------------------------------------------------------- 1 | /// Memoizes a 1-ary function. 2 | /// 3 | /// Memoization is a useful technique to cache already computed values, specially in functions with a high computational cost. It requires the input parameter to the function to be `Hashable` in order to be able to save the computed result. 4 | /// This function returns a memoized function that behaves the same as the original one. Given an input, first invokation of the memoized function will compute the result and store it. Subsequent invokations with the same input will not be computed; the stored result will be returned instead. 5 | 6 | /// - Parameters: 7 | /// - f: Function to be memoized. This function must be pure and deterministic in order to have consistent results. 8 | /// - a: Function input. 9 | /// - Returns: A function that behaves like `f` but saves already computed results. 10 | public func memoize( 11 | _ f: @escaping (_ a: A) -> B 12 | ) -> (A) -> B { 13 | var cached = Dictionary() 14 | 15 | return { a in 16 | if let cachedResult = cached[a] { 17 | return cachedResult 18 | } 19 | let result = f(a) 20 | cached[a] = result 21 | return result 22 | } 23 | } 24 | 25 | /// Memoizes a recursive 1-ary function. 26 | /// 27 | /// In order to memoize a recursive function, the recursive step must be memoized as well. In order to do so, callers of this function must pass a function that will receive the memoized function and the current input, and use both to provide the output. Input parameters must conform to `Hashable`. 28 | /// As an example, consider this implementation of a memoized factorial: 29 | /// 30 | /// let memoizedFactorial: (Int) -> Int = memoize { factorial, x in 31 | /// x == 0 ? 1 : x * factorial(x - 1) 32 | /// } 33 | /// 34 | /// - Parameters: 35 | /// - f: Function to be memoized. 36 | /// - step: A closure describing a recursive step of the function. 37 | /// - a: Input to the recursive step. 38 | /// - input: Current value for the recursion. 39 | /// - Returns: A function that behaves like `f` but saves already computed results. 40 | public func memoize( 41 | _ f: @escaping (_ step: (_ a: A) -> B, _ input: A) -> B 42 | ) -> (A) -> B { 43 | var cached = Dictionary() 44 | 45 | func wrap(_ a: A) -> B { 46 | if let cachedResult = cached[a] { 47 | return cachedResult 48 | } 49 | let result = f(wrap, a) 50 | cached[a] = result 51 | return result 52 | } 53 | 54 | return wrap 55 | } 56 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Syntax/Predef.swift: -------------------------------------------------------------------------------- 1 | /// Identity function. 2 | /// 3 | /// Returns the input without changing it. 4 | /// 5 | /// - Parameter a: A value. 6 | /// - Returns: The value received as input, with no modifications. 7 | public func id(_ a: A) -> A { 8 | a 9 | } 10 | 11 | /// Provides a constant function. 12 | /// 13 | /// - Parameter a: Constant value to return. 14 | /// - Returns: A 0-ary function that constantly return the value provided as argument. 15 | public func constant(_ a: @autoclosure @escaping () -> A) -> () -> A { 16 | a 17 | } 18 | 19 | /// Provides a constant function. 20 | /// 21 | /// - Parameter a: Constant value to return. 22 | /// - Returns: A 1-ary function that constantly return the value provided as argument, regardless of its input parameter. 23 | public func constant(_ a: @autoclosure @escaping () -> A) -> (B) -> A { 24 | { _ in a() } 25 | } 26 | 27 | /// Provides a constant function. 28 | /// 29 | /// - Parameter a: Constant value to return. 30 | /// - Returns: A 2-ary function that constantly return the value provided as argument, regardless of its input parameter. 31 | public func constant(_ a: @autoclosure @escaping () -> A) -> (B, C) -> A { 32 | { _, _ in a() } 33 | } 34 | 35 | /// Provides a constant function. 36 | /// 37 | /// - Parameter a: Constant value to return. 38 | /// - Returns: A 3-ary function that constantly return the value provided as argument, regardless of its input parameter. 39 | public func constant(_ a: @autoclosure @escaping () -> A) -> (B, C, D) -> A { 40 | { _, _, _ in a() } 41 | } 42 | 43 | /// Provides a constant function. 44 | /// 45 | /// - Parameter a: Constant value to return. 46 | /// - Returns: A 4-ary function that constantly return the value provided as argument, regardless of its input parameter. 47 | public func constant(_ a: @autoclosure @escaping () -> A) -> (B, C, D, E) -> A { 48 | { _, _, _, _ in a() } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Typeclasses/Monoid.swift: -------------------------------------------------------------------------------- 1 | /// A monoid is an algebraic structure that has the same properties as a semigroup, with an empty element. 2 | public protocol Monoid: Semigroup { 3 | /// Empty element. 4 | /// 5 | /// The empty element must obey the monoid laws: 6 | /// 7 | /// a.combine(.empty) == .empty.combine(a) == a 8 | /// 9 | /// That is, combining any element with `empty` must return the original element. 10 | /// 11 | /// - Returns: A value of the implementing type satisfying the monoid laws. 12 | static var empty: Self { get } 13 | } 14 | 15 | // MARK: Monoid syntax 16 | public extension Monoid { 17 | /// Combines a variable number of values of the implementing type, in the order provided in the call. 18 | /// 19 | /// - Parameters: 20 | /// - elements: Values of the implementing type. 21 | /// - Returns: A single value of the implementing type representing the combination of all the parameter values. 22 | static func combineAll(_ elements: Self...) -> Self { 23 | combineAll(elements) 24 | } 25 | 26 | /// Combines an array of values of the implementing type, in the order provided in the call. 27 | /// 28 | /// - Parameters: 29 | /// - elements: Values of the implementing type. 30 | /// - Returns: A single value of the implementing type representing the combination of all the parameter values. 31 | static func combineAll(_ elements: [Self]) -> Self { 32 | elements.reduce(Self.empty, Self.combine) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Typeclasses/Semigroup.swift: -------------------------------------------------------------------------------- 1 | /// A semigroup is an algebraic structure that consists of a set of values and an associative operation. 2 | public protocol Semigroup { 3 | /// An associative operation to combine values of the implementing type. 4 | /// 5 | /// This operation must satisfy the semigroup laws: 6 | /// 7 | /// a.combine(b).combine(c) == a.combine(b.combine(c)) 8 | /// 9 | /// - Parameter other: Value to combine with the receiver. 10 | /// - Returns: Combination of the receiver value with the parameter value. 11 | func combine(_ other: Self) -> Self 12 | } 13 | 14 | public extension Semigroup { 15 | /// Combines two values of the implementing type. 16 | /// 17 | /// - Parameters: 18 | /// - a: Value of the implementing type. 19 | /// - b: Value of the implementing type. 20 | /// - Returns: Combination of `a` and `b`. 21 | static func combine(_ a: Self, _ b: Self) -> Self { 22 | a.combine(b) 23 | } 24 | 25 | /// Combines a variable number of values (at least one) of the implementing type, in the order provided in the call. 26 | /// 27 | /// - Parameters: 28 | /// - first: First value of the implementing type. 29 | /// - rest: Values of the implementing type. 30 | /// - Returns: A single value of the implementing type representing the combination of all the parameter values. 31 | static func combineAll(_ first: Self, _ rest: Self...) -> Self { 32 | combineAll(first, rest) 33 | } 34 | 35 | /// Combines an array of values of the implementing type, in the order provided in the call. 36 | /// 37 | /// - Parameters: 38 | /// - first: First value of the implementing type. 39 | /// - elems: Values of the implementing type. 40 | /// - Returns: A single value of the implementing type representing the combination of all the parameter values. 41 | static func combineAll(_ first: Self, _ rest: [Self]) -> Self { 42 | rest.reduce(first, Self.combine) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/BowLiteCore/Typeclasses/Semiring.swift: -------------------------------------------------------------------------------- 1 | /// A semiring is an algebraic structure that has the same properties as a commutative monoid for addition, with multiplication. 2 | public protocol Semiring: Monoid { 3 | /// An associative operation to combine values of the implementing type. 4 | /// 5 | /// This operation must satisfy the semigroup laws: 6 | /// 7 | /// a.multiply(b).multiply(c) == a.multiply(b.multiply(c)) 8 | /// 9 | /// - Parameter other: Value to multipy with the receiver. 10 | /// - Returns: Multiplication of the receiver value with the parameter value. 11 | func multiply(_ other: Self) -> Self 12 | 13 | /// One element. 14 | /// 15 | /// The one element must obey the semirings laws: 16 | /// 17 | /// a.multiply(.one) == .one.multiply(a) == a 18 | /// 19 | /// That is, multiplying any element with `one` must return the original element. 20 | /// 21 | /// - Returns: A value of the implementing type satisfying the semiring laws. 22 | static var one: Self { get } 23 | } 24 | 25 | public extension Semiring { 26 | /// Zero element. 27 | /// 28 | /// The zero element must obey the semirings laws: 29 | /// 30 | /// a.multiply(.zero)) == .zero.multiply(a) == .zero 31 | /// 32 | /// That is, multiplying any element with `zero` must return the `zero`. 33 | /// It is also an alias for `empty` of `Monoid`. 34 | /// 35 | /// - Returns: A value of the implementing type satisfying the semiring laws. 36 | static var zero: Self { 37 | Self.empty 38 | } 39 | 40 | /// An associative operation to combine values of the implementing type. 41 | /// 42 | /// This operation must satisfy the semigroup laws: 43 | /// 44 | /// a.add(b).add(c) == a.add(b.add(c)) 45 | /// 46 | /// - Parameter other: Value to combine with the receiver. 47 | /// - Returns: Combination of the receiver value with the parameter value. 48 | func add(_ other: Self) -> Self { 49 | self.combine(other) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/Data/Array+TraverseIO.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension Array { 5 | /// Maps each element of this structure to an effect, evaluates them from left to right and collects the results. 6 | /// 7 | /// - Parameters: 8 | /// - f: A function producing an effect. 9 | /// - Returns: Results collected under the context of the effect provided by the function. 10 | func traverse(_ f: @escaping (Element) -> IO) -> IO { 11 | self.reduce(.pure([])) { partial, next in 12 | .map(partial, f(next)) { array, a in 13 | array + [a] 14 | } 15 | } 16 | } 17 | 18 | /// Evaluate each effect in this structure of values and collects the results. 19 | /// 20 | /// - Returns: Results collected under the context of the inner effects. 21 | func sequence() -> IO where Element == IO { 22 | traverse(id) 23 | } 24 | 25 | /// A traverse followed by flattening the inner result. 26 | /// 27 | /// - Parameters: 28 | /// - f: A transforming function yielding nested effects. 29 | /// - Returns: Results collected and flattened under the context of the effects. 30 | func flatTraverse(_ f: @escaping (Element) -> IO) -> IO { 31 | traverse(f).map { array in array.flatten() } 32 | } 33 | 34 | // MARK: Traverse for EnvIO 35 | 36 | /// Maps each element of this structure to an effect, evaluates them from left to right and collects the results. 37 | /// 38 | /// - Parameters: 39 | /// - f: A function producing an effect. 40 | /// - Returns: Results collected under the context of the effect provided by the function. 41 | func traverse(_ f: @escaping (Element) -> EnvIO) -> EnvIO { 42 | self.reduce(.pure([])) { partial, next in 43 | .map(partial, f(next)) { array, a in 44 | array + [a] 45 | } 46 | } 47 | } 48 | 49 | /// Evaluate each effect in this structure of values and collects the results. 50 | /// 51 | /// - Returns: Results collected under the context of the inner effects. 52 | func sequence() -> EnvIO where Element == EnvIO { 53 | traverse(id) 54 | } 55 | 56 | /// A traverse followed by flattening the inner result. 57 | /// 58 | /// - Parameters: 59 | /// - f: A transforming function yielding nested effects. 60 | /// - Returns: Results collected and flattened under the context of the effects. 61 | func flatTraverse(_ f: @escaping (Element) -> EnvIO) -> EnvIO { 62 | traverse(f).map { array in array.flatten() } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/Data/NonEmptyArray+TraverseIO.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension NonEmptyArray { 5 | /// Maps each element of this structure to an effect, evaluates them from left to right and collects the results. 6 | /// 7 | /// - Parameters: 8 | /// - f: A function producing an effect. 9 | /// - Returns: Results collected under the context of the effect provided by the function. 10 | func traverse(_ f: @escaping (Element) -> IO) -> IO> { 11 | self.asArray.traverse(f).map(NEA.init(unsafe:)) 12 | } 13 | 14 | /// Evaluate each effect in this structure of values and collects the results. 15 | /// 16 | /// - Returns: Results collected under the context of the inner effects. 17 | func sequence() -> IO> where Element == IO { 18 | traverse(id) 19 | } 20 | 21 | /// A traverse followed by flattening the inner result. 22 | /// 23 | /// - Parameters: 24 | /// - f: A transforming function yielding nested effects. 25 | /// - Returns: Results collected and flattened under the context of the effects. 26 | func flatTraverse(_ f: @escaping (Element) -> IO>) -> IO> { 27 | traverse(f).map { array in array.flatten() } 28 | } 29 | 30 | // MARK: Traverse for EnvIO 31 | 32 | /// Maps each element of this structure to an effect, evaluates them from left to right and collects the results. 33 | /// 34 | /// - Parameters: 35 | /// - f: A function producing an effect. 36 | /// - Returns: Results collected under the context of the effect provided by the function. 37 | func traverse(_ f: @escaping (Element) -> EnvIO) -> EnvIO> { 38 | self.asArray.traverse(f).map(NEA.init(unsafe:)) 39 | } 40 | 41 | /// Evaluate each effect in this structure of values and collects the results. 42 | /// 43 | /// - Returns: Results collected under the context of the inner effects. 44 | func sequence() -> EnvIO> where Element == EnvIO { 45 | traverse(id) 46 | } 47 | 48 | /// A traverse followed by flattening the inner result. 49 | /// 50 | /// - Parameters: 51 | /// - f: A transforming function yielding nested effects. 52 | /// - Returns: Results collected and flattened under the context of the effects. 53 | func flatTraverse(_ f: @escaping (Element) -> EnvIO>) -> EnvIO> { 54 | traverse(f).map { array in array.flatten() } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/EnvIO/EnvIO+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension EnvIO { 5 | /// Creates a value of this type from an Either. 6 | /// 7 | /// - Parameter either: Either value to convert to this type. 8 | /// - Returns: A value that represents the same content from Either, in this context. 9 | static func from(either: Either) -> EnvIO { 10 | either.fold(EnvIO.raiseError, EnvIO.pure) 11 | } 12 | 13 | /// Creates a value of this type from a Result. 14 | /// 15 | /// - Parameter result: Result value to convert to this type. 16 | /// - Returns: A value that represents the same content from Result, in this context. 17 | static func from(result: Result) -> EnvIO { 18 | result.fold(EnvIO.pure, EnvIO.raiseError) 19 | } 20 | 21 | /// Handles an error, potentially recovering from it by mapping it to a value. 22 | /// 23 | /// - Parameters: 24 | /// - f: A recovery function. 25 | /// - Returns: A value where the possible errors have been recovered using the provided function. 26 | func handleError(_ f: @escaping (Failure) -> Success) -> EnvIO { 27 | handleErrorWith(f >>> EnvIO.pure) 28 | } 29 | 30 | /// Applies a monadic function to an effect discarding the output. 31 | /// 32 | /// - Parameters: 33 | /// - fa: A computation. 34 | /// - f: A monadic function which result will be discarded. 35 | /// - Returns: A computation with the effect of the initial computation. 36 | func flatTapError( 37 | _ f: @escaping (Failure) -> EnvIO) -> EnvIO { 38 | self.handleErrorWith { e in 39 | f(e).handleErrorWith { _ in .raiseError(e) } 40 | .followedBy(.raiseError(e)) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/EnvIO/EnvIO+Async.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension EnvIO { 5 | /// Suspends side effects in the provided registration function. The parameter function is injected with a side-effectful callback for signaling the result of an asynchronous process. 6 | /// 7 | /// - Parameter proc: Asynchronous operation. 8 | /// - Returns: A computation describing the asynchronous operation. 9 | static func async(_ proc: @escaping IOProc) -> EnvIO { 10 | asyncF { callback in 11 | EnvIO.later { 12 | proc(callback) 13 | } 14 | } 15 | } 16 | 17 | /// Provides a computation that evaluates the provided function on every run. 18 | /// 19 | /// - Parameter queue: Dispatch queue which the computation must be sent to. 20 | /// - Parameter f: Function returning a value. 21 | /// - Returns: A computation that defers the execution of the provided function. 22 | static func `defer`( 23 | _ queue: DispatchQueue, 24 | _ f: @escaping () -> EnvIO) -> EnvIO { 25 | EnvIO.lazy().continueOn(queue).flatMap(f) 26 | } 27 | 28 | /// Provides a computation that evaluates the provided function on every run. 29 | /// 30 | /// - Parameter queue: Dispatch queue which the computation must be sent to. 31 | /// - Parameter f: Function returning a value. 32 | /// - Returns: A computation that defers the execution of the provided function. 33 | static func later( 34 | _ queue: DispatchQueue, 35 | _ f: @escaping () throws -> Success) -> EnvIO { 36 | Self.defer(queue) { 37 | do { 38 | return pure(try f()) 39 | } catch { 40 | return raiseError(error as! Failure) 41 | } 42 | } 43 | } 44 | 45 | /// Provides a computation that evaluates the provided function on every run. 46 | /// 47 | /// - Parameter queue: Dispatch queue which the computation must be sent to. 48 | /// - Parameter f: A function that provides a value or an error. 49 | /// - Returns: A computation that defers the execution of the provided value. 50 | static func delayOrRaise( 51 | _ queue: DispatchQueue, 52 | _ f: @escaping () -> Either 53 | ) -> EnvIO { 54 | Self.defer(queue) { f().fold(Self.raiseError, Self.pure) } 55 | } 56 | 57 | /// Provides an asynchronous computation that never finishes. 58 | /// 59 | /// - Returns: An asynchronous computation that never finishes. 60 | static func never() -> EnvIO { 61 | async { _ in } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/EnvIO/EnvIO+Functor.swift: -------------------------------------------------------------------------------- 1 | import BowLiteCore 2 | 3 | public extension EnvIO { 4 | /// Given a function, provides a new function lifted to the context of this type. 5 | /// 6 | /// - Parameter f: Function to be lifted. 7 | /// - Returns: Function in the context of this type. 8 | static func lift(_ f: @escaping (Success) -> B) -> (EnvIO) -> EnvIO { 9 | { either in either.map(f) } 10 | } 11 | 12 | /// Transforms the value type with a constant value. 13 | /// 14 | /// - Parameters: 15 | /// - b: Constant value to replace the value type. 16 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 17 | func `as`(_ b: B) -> EnvIO { 18 | self.map(constant(b)) 19 | } 20 | 21 | /// Replaces the value type by the `Void` type. 22 | /// 23 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 24 | func void() -> EnvIO { 25 | self.as(()) 26 | } 27 | 28 | /// Transforms the value type and pairs it with its original value. 29 | /// 30 | /// - Parameters: 31 | /// - f: Transforming function. 32 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 33 | func product(_ f: @escaping (Success) -> B) -> EnvIO { 34 | self.map { a in (a, f(a)) } 35 | } 36 | 37 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 38 | /// 39 | /// - Parameters: 40 | /// - b: Constant value for the tuple. 41 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 42 | func tupleLeft(_ b: B) -> EnvIO { 43 | self.map { a in (b, a) } 44 | } 45 | 46 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 47 | /// 48 | /// - Parameters: 49 | /// - b: Constant value for the tuple. 50 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 51 | func tupleRight(_ b: B) -> EnvIO { 52 | self.map { a in (a, b) } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/EnvIO/EnvIO+MonadDefer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension EnvIO { 5 | /// Provides a computation that evaluates the provided function on every run. 6 | /// 7 | /// - Parameter f: Function returning a value. 8 | /// - Returns: A computation that defers the execution of the provided function. 9 | static func later(_ f: @escaping () throws -> Success) -> EnvIO { 10 | Self.defer { 11 | do { 12 | return pure(try f()) 13 | } catch { 14 | return raiseError(error as! Failure) 15 | } 16 | } 17 | } 18 | 19 | /// Provides a computation that evaluates the provided computation on every run. 20 | /// 21 | /// - Parameter value: A value describing a computation to be deferred. 22 | /// - Returns: A computation that defers the execution of the provided value. 23 | static func later(_ value: EnvIO) -> EnvIO { 24 | Self.defer { value } 25 | } 26 | 27 | /// Provides a computation that evaluates the provided function on every run. 28 | /// 29 | /// - Parameter f: A function that provides a value or an error. 30 | /// - Returns: A computation that defers the execution of the provided value. 31 | static func laterOrRaise(_ f: @escaping () -> Either) -> EnvIO { 32 | self.defer { 33 | f().fold(Self.raiseError, Self.pure) 34 | } 35 | } 36 | } 37 | 38 | public extension EnvIO where Success == Void { 39 | /// Provides a lazy computation that returns void. 40 | /// 41 | /// - Returns: A deferred computation of the void value. 42 | static func lazy() -> EnvIO { 43 | later {} 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/EnvIO/EnvIO+MonadReader.swift: -------------------------------------------------------------------------------- 1 | public extension EnvIO where Dependencies == Success { 2 | /// Retrieves the shared environment. 3 | /// 4 | /// - Returns: Shared environment. 5 | static func ask() -> EnvIO { 6 | EnvIO { dependencies in IO.pure(dependencies) } 7 | } 8 | } 9 | 10 | public extension EnvIO { 11 | /// Executes this computation in a modified environment. 12 | /// 13 | /// - Parameters: 14 | /// - f: Funtion to modify the environment. 15 | /// - Returns: Computation in the modified environment. 16 | func local(_ f: @escaping (Dependencies) -> Dependencies) -> EnvIO { 17 | self.contramap(f) 18 | } 19 | 20 | /// Retrieves a function of the current environment. 21 | /// 22 | /// - Parameter f: Selector function to apply to the environment. 23 | /// - Returns: A value extracted from the environment, in this context. 24 | static func reader(_ f: @escaping (Dependencies) -> IO) -> EnvIO { 25 | EnvIO(f) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/Foundation/ConsoleIO.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Utilities to read and write to the standard input/output in a functional manner. 4 | public enum ConsoleIO { 5 | /// IO suspended version of `Swift.print(_:separator:terminator:)`. Refer to that function for further documentation. 6 | public static func print( 7 | _ line: @escaping @autoclosure () -> Any, 8 | separator: @escaping @autoclosure () -> CustomStringConvertible = " ", 9 | terminator: @escaping @autoclosure () -> CustomStringConvertible = "\n") -> IO { 10 | IO.invoke { 11 | Swift.print( 12 | line(), 13 | separator: "\(separator())", 14 | terminator: "\(terminator())") 15 | } 16 | } 17 | 18 | /// IO suspended version of `Swift.readLine(strippingNewline:)`. Refer to that function for further documentation. 19 | public static func readLine(strippingNewline: Bool = true) -> IO { 20 | IO.invoke { 21 | Swift.readLine(strippingNewline: strippingNewline) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/IO/IO+ApplicativeError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension IO { 5 | /// Creates a value of this type from an Either. 6 | /// 7 | /// - Parameter either: Either value to convert to this type. 8 | /// - Returns: A value that represents the same content from Either, in this context. 9 | static func from(either: Either) -> IO { 10 | either.fold(IO.raiseError, IO.pure) 11 | } 12 | 13 | /// Creates a value of this type from a Result. 14 | /// 15 | /// - Parameter result: Result value to convert to this type. 16 | /// - Returns: A value that represents the same content from Result, in this context. 17 | static func from(result: Result) -> IO { 18 | result.fold(IO.pure, IO.raiseError) 19 | } 20 | 21 | /// Handles an error, potentially recovering from it by mapping it to a value. 22 | /// 23 | /// - Parameters: 24 | /// - f: A recovery function. 25 | /// - Returns: A value where the possible errors have been recovered using the provided function. 26 | func handleError(_ f: @escaping (Failure) -> Success) -> IO { 27 | handleErrorWith(f >>> IO.pure) 28 | } 29 | 30 | /// Applies a monadic function to an effect discarding the output. 31 | /// 32 | /// - Parameters: 33 | /// - f: A monadic function which result will be discarded. 34 | /// - Returns: A computation with the effect of the initial computation. 35 | func flatTapError( 36 | _ f: @escaping (Failure) -> IO) -> IO { 37 | self.handleErrorWith { e in 38 | f(e).handleErrorWith { _ in .raiseError(e) } 39 | .followedBy(.raiseError(e)) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/IO/IO+Async.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension IO { 5 | /// Suspends side effects in the provided registration function. The parameter function is injected with a side-effectful callback for signaling the result of an asynchronous process. 6 | /// 7 | /// - Parameter proc: Asynchronous operation. 8 | /// - Returns: A computation describing the asynchronous operation. 9 | static func async(_ proc: @escaping IOProc) -> IO { 10 | asyncF { callback in 11 | IO.later { 12 | proc(callback) 13 | } 14 | } 15 | } 16 | 17 | /// Provides a computation that evaluates the provided function on every run. 18 | /// 19 | /// - Parameter queue: Dispatch queue which the computation must be sent to. 20 | /// - Parameter f: Function returning a value. 21 | /// - Returns: A computation that defers the execution of the provided function. 22 | static func `defer`( 23 | _ queue: DispatchQueue, 24 | _ f: @escaping () -> IO) -> IO { 25 | IO.lazy().continueOn(queue).flatMap(f) 26 | } 27 | 28 | /// Provides a computation that evaluates the provided function on every run. 29 | /// 30 | /// - Parameter queue: Dispatch queue which the computation must be sent to. 31 | /// - Parameter f: Function returning a value. 32 | /// - Returns: A computation that defers the execution of the provided function. 33 | static func later( 34 | _ queue: DispatchQueue, 35 | _ f: @escaping () throws -> Success) -> IO { 36 | Self.defer(queue) { 37 | do { 38 | return pure(try f()) 39 | } catch { 40 | return raiseError(error as! Failure) 41 | } 42 | } 43 | } 44 | 45 | /// Provides a computation that evaluates the provided function on every run. 46 | /// 47 | /// - Parameter queue: Dispatch queue which the computation must be sent to. 48 | /// - Parameter f: A function that provides a value or an error. 49 | /// - Returns: A computation that defers the execution of the provided value. 50 | static func delayOrRaise( 51 | _ queue: DispatchQueue, 52 | _ f: @escaping () -> Either 53 | ) -> IO { 54 | Self.defer(queue) { f().fold(Self.raiseError, Self.pure) } 55 | } 56 | 57 | /// Provides an asynchronous computation that never finishes. 58 | /// 59 | /// - Returns: An asynchronous computation that never finishes. 60 | static func never() -> IO { 61 | async { _ in } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/IO/IO+Functor.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension IO { 5 | /// Given a function, provides a new function lifted to the context of this type. 6 | /// 7 | /// - Parameter f: Function to be lifted. 8 | /// - Returns: Function in the context of this type. 9 | static func lift(_ f: @escaping (Success) -> B) -> (IO) -> IO { 10 | { either in either.map(f) } 11 | } 12 | 13 | /// Transforms the value type with a constant value. 14 | /// 15 | /// - Parameters: 16 | /// - b: Constant value to replace the value type. 17 | /// - Returns: A new value with the structure of the original value, with its value type transformed. 18 | func `as`(_ b: B) -> IO { 19 | self.map(constant(b)) 20 | } 21 | 22 | /// Replaces the value type by the `Void` type. 23 | /// 24 | /// - Returns: New value in this context, with `Void` as value type, preserving the original structure. 25 | func void() -> IO { 26 | self.as(()) 27 | } 28 | 29 | /// Transforms the value type and pairs it with its original value. 30 | /// 31 | /// - Parameters: 32 | /// - f: Transforming function. 33 | /// - Returns: A pair with the original value and its transformation, with the structure of the original value. 34 | func product(_ f: @escaping (Success) -> B) -> IO { 35 | self.map { a in (a, f(a)) } 36 | } 37 | 38 | /// Transforms the value type by making a tuple with a new constant value to the left of the original value type. 39 | /// 40 | /// - Parameters: 41 | /// - b: Constant value for the tuple. 42 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 43 | func tupleLeft(_ b: B) -> IO { 44 | self.map { a in (b, a) } 45 | } 46 | 47 | /// Transforms the value type by making a tuple with a new constant value to the right of the original value type. 48 | /// 49 | /// - Parameters: 50 | /// - b: Constant value for the tuple. 51 | /// - Returns: A new value with the structure of the original value, with a tuple in its value type. 52 | func tupleRight(_ b: B) -> IO { 53 | self.map { a in (a, b) } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/IO/IO+Monad.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension IO { 5 | /// Flattens this nested structure into a single layer. 6 | /// 7 | /// - Returns: Value with a single context structure. 8 | func flatten() -> IO where Success == IO { 9 | self.flatMap(id) 10 | } 11 | 12 | /// Sequentially compose two computations, discarding the value produced by the first. 13 | /// 14 | /// - Parameters: 15 | /// - fa: 2nd computation. 16 | /// - Returns: Result of running the second computation after the first one. 17 | func followedBy(_ fa: IO) -> IO { 18 | self.flatMap(constant(fa)) 19 | } 20 | 21 | /// Sequentially compose two computations, discarding the value produced by the second. 22 | /// 23 | /// - Parameters: 24 | /// - fa: 2nd computation. 25 | /// - Returns: Result produced from the first computation after both are computed. 26 | func forEffect(_ fa: IO) -> IO { 27 | self.flatMap { wrapped in fa.as(wrapped) } 28 | } 29 | 30 | /// Pair the result of a computation with the result of applying a function to such result. 31 | /// 32 | /// - Parameters: 33 | /// - f: A function to be applied to the result of the computation. 34 | /// - Returns: A tuple of the result of the computation paired with the result of the function, in this context. 35 | func mproduct(_ f: @escaping (Success) -> IO) -> IO { 36 | self.flatMap { wrapped in 37 | f(wrapped).tupleLeft(wrapped) 38 | } 39 | } 40 | 41 | /// Conditionally apply a closure based on the boolean result of this computation. 42 | /// 43 | /// - Parameters: 44 | /// - then: Closure to be applied if the computation evaluates to `true`. 45 | /// - else: Closure to be applied if the computation evaluates to `false`. 46 | /// - Returns: Result of applying the corresponding closure based on the result of the computation. 47 | func `if`( 48 | then f: @escaping () -> IO, 49 | else g: @escaping () -> IO 50 | ) -> IO where Success == Bool { 51 | self.flatMap { boolean in 52 | boolean ? f() : g() 53 | } 54 | } 55 | 56 | /// Applies a monadic function and discard the result while keeping the effect. 57 | /// 58 | /// - Parameters: 59 | /// - f: A monadic function which result will be discarded. 60 | /// - Returns: A computation with the result of the initial computation and the effect caused by the function application. 61 | func flatTap(_ f: @escaping (Success) -> IO) -> IO { 62 | self.flatMap { wrapped in f(wrapped).as(wrapped) } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/IO/IO+MonadDefer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | public extension IO { 5 | /// Provides a computation that evaluates the provided function on every run. 6 | /// 7 | /// - Parameter f: Function returning a value. 8 | /// - Returns: A computation that defers the execution of the provided function. 9 | static func later(_ f: @escaping () throws -> Success) -> IO { 10 | Self.defer { 11 | do { 12 | return pure(try f()) 13 | } catch { 14 | return raiseError(error as! Failure) 15 | } 16 | } 17 | } 18 | 19 | /// Provides a computation that evaluates the provided computation on every run. 20 | /// 21 | /// - Parameter value: A value describing a computation to be deferred. 22 | /// - Returns: A computation that defers the execution of the provided value. 23 | static func later(_ value: IO) -> IO { 24 | Self.defer { value } 25 | } 26 | 27 | /// Provides a computation that evaluates the provided function on every run. 28 | /// 29 | /// - Parameter f: A function that provides a value or an error. 30 | /// - Returns: A computation that defers the execution of the provided value. 31 | static func laterOrRaise(_ f: @escaping () -> Either) -> IO { 32 | self.defer { 33 | f().fold(Self.raiseError, Self.pure) 34 | } 35 | } 36 | } 37 | 38 | public extension IO where Success == Void { 39 | /// Provides a lazy computation that returns void. 40 | /// 41 | /// - Returns: A deferred computation of the void value. 42 | static func lazy() -> IO { 43 | later {} 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/Internal/DispatchTimeInterval+Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension DispatchTimeInterval { 4 | func toDouble() -> Double? { 5 | switch self { 6 | case .seconds(let value): return Double(value) 7 | case .milliseconds(let value): return Double(value) * 0.001 8 | case .microseconds(let value): return Double(value) * 0.000_001 9 | case .nanoseconds(let value): return Double(value) * 0.000_000_001 10 | case .never: return nil 11 | @unknown default: return nil 12 | } 13 | } 14 | } 15 | 16 | func >=(lhs: DispatchTimeInterval, rhs: DispatchTimeInterval) -> Bool { 17 | let now = DispatchTime.now() 18 | return now + lhs >= now + rhs 19 | } 20 | 21 | func -(lhs: DispatchTime, rhs: DispatchTime) -> DispatchTimeInterval { 22 | let l = Int(lhs.rawValue) 23 | let r = Int(rhs.rawValue) 24 | return .nanoseconds(Int(l - r)) 25 | } 26 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/Internal/Queue.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | internal struct Queue { 4 | static func queue(_ queue: DispatchQueue = .main) -> Queue { 5 | queue.setSpecific(key: Key.threadLabel, value: queue.label) 6 | return .init(queue: ._queue(queue)) 7 | } 8 | 9 | static func queue(label: String, qos: DispatchQoS = .default) -> Queue { 10 | queue(DispatchQueue(label: label, qos: qos)) 11 | } 12 | 13 | static var current: Queue { 14 | .init(queue: ._current) 15 | } 16 | 17 | // MARK: cases 18 | private let queue: _Queue 19 | 20 | private enum _Queue { 21 | case _queue(DispatchQueue) 22 | case _current 23 | } 24 | 25 | // MARK: properties 26 | var label: String { 27 | switch queue { 28 | case ._queue(let queue): 29 | return queue.label 30 | case ._current: 31 | return DispatchQueue.currentLabel 32 | } 33 | } 34 | 35 | var qos: DispatchQoS { 36 | switch queue { 37 | case ._queue(let queue): 38 | return queue.qos 39 | case ._current: 40 | return .default 41 | } 42 | } 43 | 44 | // MARK: operations 45 | func async(execute work: @escaping () -> Void) { 46 | switch queue { 47 | case ._queue(let queue): 48 | queue.async(execute: work) 49 | default: 50 | fatalError("can not execute async work in current queue") 51 | } 52 | } 53 | 54 | func sync(execute work: () throws -> T) rethrows -> T { 55 | switch queue { 56 | case ._queue(let queue): 57 | if DispatchQueue.currentLabel == queue.label { 58 | return try work() 59 | } else { 60 | return try queue.sync(execute: work) 61 | } 62 | case ._current: 63 | return try work() 64 | } 65 | } 66 | 67 | 68 | fileprivate enum Key { 69 | static let threadLabel = DispatchSpecificKey() 70 | } 71 | } 72 | 73 | 74 | // MARK: - helpers 75 | internal extension DispatchQueue { 76 | var queue: Queue { 77 | .queue(self) 78 | } 79 | } 80 | 81 | public extension DispatchQueue { 82 | static var currentLabel: String { 83 | DispatchQueue.getSpecific(key: Queue.Key.threadLabel) ?? "unknown-\(Date().timeIntervalSince1970)" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/Kleisli/Kleisli+IO.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | // MARK: Kleisli for IO 5 | 6 | /// Composes two effectful functions. 7 | /// 8 | /// - Parameters: 9 | /// - f: Left-hand side of the composition. 10 | /// - g: Right-hand side of the composition. 11 | /// - Returns: A function resulting from the Kleisli composition of the two effectful functions provided. 12 | public func andThen( 13 | _ f: @escaping (A) -> IO, 14 | _ g: @escaping (B) -> IO 15 | ) -> (A) -> IO { 16 | { a in 17 | f(a).flatMap(g) 18 | } 19 | } 20 | 21 | /// Composes two effectful functions. 22 | /// 23 | /// - Parameters: 24 | /// - f: Left-hand side of the composition. 25 | /// - g: Right-hand side of the composition. 26 | /// - Returns: A function resulting from the Kleisli composition of the two effectful functions provided. 27 | public func >=>( 28 | _ f: @escaping (A) -> IO, 29 | _ g: @escaping (B) -> IO 30 | ) -> (A) -> IO { 31 | andThen(f, g) 32 | } 33 | 34 | // MARK: Kleisli for EnvIO 35 | 36 | /// Composes two effectful functions. 37 | /// 38 | /// - Parameters: 39 | /// - f: Left-hand side of the composition. 40 | /// - g: Right-hand side of the composition. 41 | /// - Returns: A function resulting from the Kleisli composition of the two effectful functions provided. 42 | public func andThen( 43 | _ f: @escaping (A) -> EnvIO, 44 | _ g: @escaping (B) -> EnvIO 45 | ) -> (A) -> EnvIO { 46 | { a in 47 | f(a).flatMap(g) 48 | } 49 | } 50 | 51 | /// Composes two effectful functions. 52 | /// 53 | /// - Parameters: 54 | /// - f: Left-hand side of the composition. 55 | /// - g: Right-hand side of the composition. 56 | /// - Returns: A function resulting from the Kleisli composition of the two effectful functions provided. 57 | public func >=>( 58 | _ f: @escaping (A) -> EnvIO, 59 | _ g: @escaping (B) -> EnvIO 60 | ) -> (A) -> EnvIO { 61 | andThen(f, g) 62 | } 63 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/MonadComprehensions/BindingOperator.swift: -------------------------------------------------------------------------------- 1 | infix operator <- : AssignmentPrecedence 2 | prefix operator |<- 3 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/MonadComprehensions/EnvIO/EnvIOBindingExpression.swift: -------------------------------------------------------------------------------- 1 | import BowLiteCore 2 | 3 | /// A binding expression is one of the instructions of the form `x <- fx` in a monad comprehension. 4 | /// 5 | /// In a binding expression of the form `x <- fx`, `x` is the variable to be bound and `fx` is the 6 | /// monadic effect that we want to bind to the variable.s 7 | public class EnvIOBindingExpression { 8 | internal let bound: EnvIOBoundVar 9 | internal let fa: () -> EnvIO 10 | 11 | init(_ bound: EnvIOBoundVar, _ fa: @escaping () -> EnvIO) { 12 | self.bound = bound 13 | self.fa = fa 14 | } 15 | 16 | internal func yield(_ f: @escaping () -> Value) -> EnvIO { 17 | fa().map { x in 18 | self.bound.bind(x) 19 | return f() 20 | } 21 | } 22 | 23 | internal func bind(_ partial: Eval>) -> EnvIO { 24 | fa().flatMap { x in 25 | self.bound.bind(x) 26 | return partial.value() 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/MonadComprehensions/EnvIO/EnvIOMonadComprehensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | class EnvIOMonadComprehension { 5 | static func buildBlock( 6 | _ children: [EnvIOBindingExpression], 7 | yield: @escaping () -> Value 8 | ) -> EnvIO { 9 | if let last = children.last { 10 | return Array(children.dropLast()).foldRight( 11 | Eval.always { last.yield(yield) } 12 | ) { expression, partial in 13 | Eval.always { expression.bind(partial) } 14 | }.value() 15 | } else { 16 | return .pure(yield()) 17 | } 18 | } 19 | } 20 | 21 | /// Monad comprehension. 22 | /// 23 | /// Chains multiple binding expressions in imperative-style syntax by using the `flatMap` operation, and yields a final result. 24 | /// 25 | /// - Parameters: 26 | /// - expressions: A variable number of binding expressions. 27 | /// - value: Value to be yield by the monad comprehension. 28 | /// - Returns: An effect resulting from the chaining of all the effects included in this monad comprehension. 29 | public func binding( 30 | _ expressions: EnvIOBindingExpression..., 31 | yield value: @autoclosure @escaping () -> Value 32 | ) -> EnvIO { 33 | EnvIOMonadComprehension.buildBlock(expressions, yield: value) 34 | } 35 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/MonadComprehensions/IO/IOBindingExpression.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | /// A binding expression is one of the instructions of the form `x <- fx` in a monad comprehension. 5 | /// 6 | /// In a binding expression of the form `x <- fx`, `x` is the variable to be bound and `fx` is the 7 | /// monadic effect that we want to bind to the variable. 8 | public class IOBindingExpression { 9 | internal let bound: IOBoundVar 10 | internal let fa: () -> IO 11 | 12 | init(_ bound: IOBoundVar, _ fa: @escaping () -> IO) { 13 | self.bound = bound 14 | self.fa = fa 15 | } 16 | 17 | internal func yield(_ f: @escaping () -> Value) -> IO { 18 | fa().map { x in 19 | self.bound.bind(x) 20 | return f() 21 | } 22 | } 23 | 24 | internal func bind(_ partial: Eval>) -> IO { 25 | fa().flatMap { x in 26 | self.bound.bind(x) 27 | return partial.value() 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/BowLiteEffects/MonadComprehensions/IO/IOMonadComprehensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import BowLiteCore 3 | 4 | class IOMonadComprehension { 5 | static func buildBlock( 6 | _ children: [IOBindingExpression], 7 | yield: @escaping () -> Value 8 | ) -> IO { 9 | if let last = children.last { 10 | return Array(children.dropLast()).foldRight( 11 | Eval.always { last.yield(yield) } 12 | ) { expression, partial in 13 | Eval.always { expression.bind(partial) } 14 | }.value() 15 | } else { 16 | return .pure(yield()) 17 | } 18 | } 19 | } 20 | 21 | /// Monad comprehension. 22 | /// 23 | /// Chains multiple binding expressions in imperative-style syntax by using the `flatMap` operation, and yields a final result. 24 | /// 25 | /// - Parameters: 26 | /// - expressions: A variable number of binding expressions. 27 | /// - value: Value to be yield by the monad comprehension. 28 | /// - Returns: An effect resulting from the chaining of all the effects included in this monad comprehension. 29 | public func binding( 30 | _ expressions: IOBindingExpression..., 31 | yield value: @autoclosure @escaping () -> Value 32 | ) -> IO { 33 | IOMonadComprehension.buildBlock(expressions, yield: value) 34 | } 35 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Array/Array+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ArrayFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Array) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Array, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Array, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Array, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Array, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Array/Array+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ArrayMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Array.pure 10 | 11 | return Array.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Array.pure(a) 18 | return fa.flatMap(Array.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Array.pure 25 | 26 | return ({ (n: Int) in Array.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Array.pure 33 | 34 | return (g >=> Array.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Array.pure 41 | let fa = Array.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Array>.pure(Array.pure(a)).flatten() 50 | == 51 | Array.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Const/Const+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Const: Arbitrary where Constant: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Constant.arbitrary.map(Const.init) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Const/Const+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ConstFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Const) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Const, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("Void") <~ forAll() { (fa: Const, f: ArrowOf) in 22 | fa.void() == fa.map(f.getArrow).void() 23 | } 24 | 25 | property("product") <~ forAll { (fa: Const, f: ArrowOf) in 26 | 27 | fa.product(f.getArrow).map { x in x.1 } 28 | == 29 | fa.map(f.getArrow) 30 | } 31 | 32 | property("tuple left") <~ forAll { (fa: Const, b: Int) in 33 | 34 | fa.tupleLeft(b).map { x in x.0 } 35 | == 36 | fa.as(b) 37 | } 38 | 39 | property("tuple right") <~ forAll { (fa: Const, b: Int) in 40 | 41 | fa.tupleRight(b).map { x in x.1 } 42 | == 43 | fa.as(b) 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Either/Either+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class EitherApplicativeErrorTest: XCTestCase { 7 | func testApplicativeErrorLaws() { 8 | property("Applicative error handle") <~ forAll { (error: AnyError, f: ArrowOf) in 9 | 10 | Either.raiseError(error).handleError(f.getArrow) 11 | == 12 | Either.pure(f.getArrow(error)) 13 | } 14 | 15 | property("Applicative error handle with") <~ forAll { (f: ArrowOf>, error: AnyError) in 16 | 17 | Either.raiseError(error).handleErrorWith(f.getArrow) 18 | == 19 | f.getArrow(error) 20 | } 21 | 22 | property("Applicative error handle with pure") <~ forAll { (a: Int, f: ArrowOf>) in 23 | 24 | Either.pure(a).handleErrorWith(f.getArrow) 25 | == 26 | Either.pure(a) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Either/Either+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Either: Arbitrary where Left: Arbitrary, Right: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | let left = Left.arbitrary.map(Either.left) 8 | let right = Right.arbitrary.map(Either.right) 9 | return Gen.one(of: [left, right]) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Either/Either+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class EitherFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Either) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Either, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Either, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Either, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Either, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Either/Either+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class EitherMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Either.pure 10 | 11 | return Either.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Either.pure(a) 18 | return fa.flatMap(Either.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Either.pure 25 | 26 | return ({ (n: Int) in Either.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Either.pure 33 | 34 | return (g >=> Either.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Either.pure 41 | let fa = Either.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Either>.pure(Either.pure(a)).flatten() 50 | == 51 | Either.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Eval/Eval+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | // Eval cannot conform to Arbitrary as it is not a final class 6 | 7 | private let now = Int.arbitrary.map(Eval.now) 8 | private let later = Int.arbitrary.map { value in Eval.later { value } } 9 | private let always = Int.arbitrary.map { value in Eval.always { value } } 10 | let evalGen = Gen.one(of: [now, later, always]) 11 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Eval/Eval+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class EvalFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAllNoShrink(evalGen) { (fa: Eval) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAllNoShrink(evalGen, ArrowOf.arbitrary, ArrowOf.arbitrary) { (fa: Eval, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAllNoShrink(evalGen, ArrowOf.arbitrary) { (fa: Eval, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAllNoShrink(evalGen, Int.arbitrary) { (fa: Eval, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAllNoShrink(evalGen, Int.arbitrary) { (fa: Eval, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Eval/Eval+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class EvalMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Eval.pure 10 | 11 | return Eval.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Eval.pure(a) 18 | return fa.flatMap(Eval.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Eval.pure 25 | 26 | return ({ (n: Int) in Eval.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Eval.pure 33 | 34 | return (g >=> Eval.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Eval.pure 41 | let fa = Eval.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Eval>.pure(Eval.pure(a)).flatten() 50 | == 51 | Eval.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Function/Function+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Function: Arbitrary where Input: Hashable & CoArbitrary, Output: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | ArrowOf.arbitrary.map { arrow in 8 | Function(arrow.getArrow) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Function/Function+Equatable.swift: -------------------------------------------------------------------------------- 1 | import BowLiteCore 2 | import BowLiteLaws 3 | 4 | extension Function: Equatable where Input == Int, Output: Equatable { 5 | public static func ==(lhs: Function, rhs: Function) -> Bool { 6 | lhs.invoke(0) == rhs.invoke(0) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Function/Function+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class FunctionFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Function) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Function, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Function, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Function, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Function, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Function/Function+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class FunctionMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Function.pure 10 | 11 | return Function.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Function.pure(a) 18 | return fa.flatMap(Function.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Function.pure 25 | 26 | return ({ (n: Int) in Function.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Function.pure 33 | 34 | return (g >=> Function.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Function.pure 41 | let fa = Function.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Function>.pure(Function.pure(a)).flatten() 50 | == 51 | Function.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Id/Id+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Id: Arbitrary where Wrapped: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Wrapped.arbitrary.map(Id.init) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Id/Id+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class IdFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Id) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Id, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Id, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Id, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Id, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Id/Id+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class IdMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Id.pure 10 | 11 | return Id.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Id.pure(a) 18 | return fa.flatMap(Id.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Id.pure 25 | 26 | return ({ (n: Int) in Id.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Id.pure 33 | 34 | return (g >=> Id.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Id.pure 41 | let fa = Id.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Id>.pure(Id.pure(a)).flatten() 50 | == 51 | Id.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Ior/Ior+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Ior: Arbitrary where Left: Arbitrary, Right: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | let left = Left.arbitrary.map(Ior.left) 8 | let right = Right.arbitrary.map(Ior.right) 9 | let both = Gen.zip(Left.arbitrary, Right.arbitrary).map(Ior.both) 10 | return Gen.one(of: [left, right, both]) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Ior/Ior+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class IorFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Ior) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Ior, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Ior, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Ior, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Ior, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Ior/Ior+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class IorMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Ior.pure 10 | 11 | return Ior.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Ior.pure(a) 18 | return fa.flatMap(Ior.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Ior.pure 25 | 26 | return ({ (n: Int) in Ior.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Ior.pure 33 | 34 | return (g >=> Ior.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Ior.pure 41 | let fa = Ior.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Ior>.pure(Ior.pure(a)).flatten() 50 | == 51 | Ior.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/NonEmptyArray/NonEmptyArray+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension NonEmptyArray: Arbitrary where Element: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Array.arbitrary.suchThat { array in 8 | array.count > 0 9 | }.map(NEA.init(unsafe:)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/NonEmptyArray/NonEmptyArray+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class NEAFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: NEA) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: NEA, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: NEA, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: NEA, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: NEA, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/NonEmptyArray/NonEmptyArray+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class NEAMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> NEA.pure 10 | 11 | return NEA.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = NEA.pure(a) 18 | return fa.flatMap(NEA.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> NEA.pure 25 | 26 | return ({ (n: Int) in NEA.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> NEA.pure 33 | 34 | return (g >=> NEA.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> NEA.pure 41 | let fa = NEA.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | NEA>.pure(NEA.pure(a)).flatten() 50 | == 51 | NEA.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Optional/Optional+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class OptionalApplicativeErrorTest: XCTestCase { 7 | func testApplicativeErrorLaws() { 8 | property("Applicative error handle") <~ forAll { (error: AnyError, f: Int) in 9 | 10 | Optional.raiseError().handleError(constant(f)) 11 | == 12 | Optional.pure(constant(f)(error)) 13 | } 14 | 15 | property("Applicative error handle with") <~ forAll { (f: Optional, error: AnyError) in 16 | 17 | Optional.raiseError().handleErrorWith(constant(f)) 18 | == 19 | constant(f)(error) 20 | } 21 | 22 | property("Applicative error handle with pure") <~ forAll { (a: Int, f: Optional) in 23 | 24 | Optional.pure(a).handleErrorWith(constant(f)) 25 | == 26 | Optional.pure(a) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Optional/Optional+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class OptionalFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Optional) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Optional, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Optional, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Optional, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Optional, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Optional/Optional+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class OptionalMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Optional.pure 10 | 11 | return Optional.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Optional.pure(a) 18 | return fa.flatMap(Optional.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Optional.pure 25 | 26 | return ({ (n: Int) in Optional.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Optional.pure 33 | 34 | return (g >=> Optional.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Optional.pure 41 | let fa = Optional.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Optional>.pure(Optional.pure(a)).flatten() 50 | == 51 | Optional.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Pair/Pair+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Pair: Arbitrary where First: Arbitrary, Second: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Gen.zip(First.arbitrary, Second.arbitrary).map { first, second in 8 | Pair(first, second) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Pair/Pair+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class PairFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Pair) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Pair, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Pair, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Pair, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Pair, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Pair/Pair+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteCore 4 | import BowLiteLaws 5 | 6 | class PairMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Pair.pure 10 | 11 | return Pair.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Pair.pure(a) 18 | return fa.flatMap(Pair.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Pair.pure 25 | 26 | return ({ (n: Int) in Pair.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Pair.pure 33 | 34 | return (g >=> Pair.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Pair.pure 41 | let fa = Pair.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Pair>.pure(Pair.pure(a)).flatten() 50 | == 51 | Pair.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Result/Result+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ResultApplicativeErrorTest: XCTestCase { 7 | func testApplicativeErrorLaws() { 8 | property("Applicative error handle") <~ forAll { (error: AnyError, f: ArrowOf) in 9 | 10 | Result.raiseError(error).handleError(f.getArrow) 11 | == 12 | Result.pure(f.getArrow(error)) 13 | } 14 | 15 | property("Applicative error handle with") <~ forAll { (f: ArrowOf>, error: AnyError) in 16 | 17 | Result.raiseError(error).handleErrorWith(f.getArrow) 18 | == 19 | f.getArrow(error) 20 | } 21 | 22 | property("Applicative error handle with pure") <~ forAll { (a: Int, f: ArrowOf>) in 23 | 24 | Result.pure(a).handleErrorWith(f.getArrow) 25 | == 26 | Result.pure(a) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Result/Result+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ResultFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Result) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Result, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Result, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Result, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Result, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Result/Result+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ResultMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Result.pure 10 | 11 | return Result.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Result.pure(a) 18 | return fa.flatMap(Result.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Result.pure 25 | 26 | return ({ (n: Int) in Result.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Result.pure 33 | 34 | return (g >=> Result.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Result.pure 41 | let fa = Result.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Result, AnyError>.pure(Result.pure(a)).flatten() 50 | == 51 | Result.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/State/State+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension State: Arbitrary where StateType: Hashable & Arbitrary & CoArbitrary, Value: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Gen.zip(ArrowOf.arbitrary, 8 | ArrowOf.arbitrary).map { f, g in 9 | State { state in 10 | (f.getArrow(state), g.getArrow(state)) 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/State/State+Equatable.swift: -------------------------------------------------------------------------------- 1 | import BowLiteCore 2 | import BowLiteLaws 3 | 4 | extension State: Equatable where StateType == Int, Value: Equatable { 5 | public static func ==(lhs: State, rhs: State) -> Bool { 6 | lhs.invoke(0) == rhs.invoke(0) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/State/State+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class StateFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: State) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: State, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: State, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: State, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: State, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/State/State+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class StateMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> State.pure 10 | 11 | return State.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = State.pure(a) 18 | return fa.flatMap(State.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> State.pure 25 | 26 | return ({ (n: Int) in State.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> State.pure 33 | 34 | return (g >=> State.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> State.pure 41 | let fa = State.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | State>.pure(State.pure(a)).flatten() 50 | == 51 | State.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Store/Store+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Store: Arbitrary where State: Hashable & Arbitrary & CoArbitrary, View: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Gen.zip(State.arbitrary, ArrowOf.arbitrary).map { state, arrow in 8 | Store(state: state, render: arrow.getArrow) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Store/Store+Equatable.swift: -------------------------------------------------------------------------------- 1 | import BowLiteCore 2 | import BowLiteLaws 3 | 4 | extension Store: Equatable where View: Equatable { 5 | public static func ==(lhs: Store, rhs: Store) -> Bool { 6 | lhs.extract() == rhs.extract() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Store/Store+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class StoreFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Store) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Store, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Store, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Store, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Store, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Try/Try+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class TryApplicativeErrorTest: XCTestCase { 7 | func testApplicativeErrorLaws() { 8 | property("Applicative error handle") <~ forAll { (error: AnyError, f: ArrowOf) in 9 | 10 | Try.raiseError(error).handleError { e in f.getArrow(e as! AnyError) } 11 | == 12 | Try.pure(f.getArrow(error)) 13 | } 14 | 15 | property("Applicative error handle with") <~ forAll { (f: ArrowOf>, error: AnyError) in 16 | 17 | Try.raiseError(error).handleErrorWith { e in f.getArrow(e as! AnyError) } 18 | == 19 | f.getArrow(error) 20 | } 21 | 22 | property("Applicative error handle with pure") <~ forAll { (a: Int, f: ArrowOf>) in 23 | 24 | Try.pure(a).handleErrorWith { e in f.getArrow(e as! AnyError) } 25 | == 26 | Try.pure(a) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Try/Try+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | 3 | import BowLiteCore 4 | import BowLiteLaws 5 | 6 | extension Try: Arbitrary where Success: Arbitrary { 7 | public static var arbitrary: Gen> { 8 | let success = Success.arbitrary.map(Try.success) 9 | let failure = AnyError.arbitrary.map(Try.failure) 10 | return Gen.one(of: [success, failure]) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Try/Try+Equatable.swift: -------------------------------------------------------------------------------- 1 | import BowLiteCore 2 | import BowLiteLaws 3 | 4 | extension Try: Equatable where Success: Equatable { 5 | public static func ==(lhs: Try, rhs: Try) -> Bool { 6 | switch (lhs, rhs) { 7 | case (.success(let l), .success(let r)): return l == r 8 | case (.failure(let l), .failure(let r)): return "\(l)" == "\(r)" 9 | default: return false 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Try/Try+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class TryFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Try) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Try, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Try, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Try, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Try, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Try/Try+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class TryMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Try.pure 10 | 11 | return Try.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Try.pure(a) 18 | return fa.flatMap(Try.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Try.pure 25 | 26 | return ({ (n: Int) in Try.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Try.pure 33 | 34 | return (g >=> Try.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Try.pure 41 | let fa = Try.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Try>.pure(Try.pure(a)).flatten() 50 | == 51 | Try.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Validated/Validated+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ValidatedApplicativeErrorTest: XCTestCase { 7 | func testApplicativeErrorLaws() { 8 | property("Applicative error handle") <~ forAll { (error: Int, f: ArrowOf) in 9 | 10 | Validated.raiseError(error).handleError(f.getArrow) 11 | == 12 | Validated.pure(f.getArrow(error)) 13 | } 14 | 15 | property("Applicative error handle with") <~ forAll { (f: ArrowOf>, error: Int) in 16 | 17 | Validated.raiseError(error).handleErrorWith(f.getArrow) 18 | == 19 | f.getArrow(error) 20 | } 21 | 22 | property("Applicative error handle with pure") <~ forAll { (a: Int, f: ArrowOf>) in 23 | 24 | Validated.pure(a).handleErrorWith(f.getArrow) 25 | == 26 | Validated.pure(a) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Validated/Validated+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Validated: Arbitrary where Invalid: Arbitrary, Valid: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | let invalid = Invalid.arbitrary.map(Validated.invalid) 8 | let valid = Valid.arbitrary.map(Validated.valid) 9 | return Gen.one(of: [invalid, valid]) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Validated/Validated+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class ValidatedFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Validated) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Validated, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Validated, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Validated, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Validated, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Writer/Writer+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteLaws 3 | import BowLiteCore 4 | 5 | extension Writer: Arbitrary where Accumulator: Arbitrary, Value: Arbitrary { 6 | public static var arbitrary: Gen> { 7 | Gen.zip(Accumulator.arbitrary, Value.arbitrary).map(Writer.init) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Writer/Writer+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class WriterFunctorTest: XCTestCase { 7 | func testFunctorLaws() { 8 | property("Identity is preserved under functor transformation") <~ forAll() { (fa: Writer) in 9 | fa.map(id) 10 | == 11 | id(fa) 12 | } 13 | 14 | property("Composition is preserved under functor transformation") <~ forAll() { (fa: Writer, f: ArrowOf, g: ArrowOf) in 15 | 16 | fa.map(f.getArrow).map(g.getArrow) 17 | == 18 | fa.map(f.getArrow >>> g.getArrow) 19 | } 20 | 21 | property("product") <~ forAll { (fa: Writer, f: ArrowOf) in 22 | 23 | fa.product(f.getArrow).map { x in x.1 } 24 | == 25 | fa.map(f.getArrow) 26 | } 27 | 28 | property("tuple left") <~ forAll { (fa: Writer, b: Int) in 29 | 30 | fa.tupleLeft(b).map { x in x.0 } 31 | == 32 | fa.as(b) 33 | } 34 | 35 | property("tuple right") <~ forAll { (fa: Writer, b: Int) in 36 | 37 | fa.tupleRight(b).map { x in x.1 } 38 | == 39 | fa.as(b) 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Data/Writer/Writer+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteLaws 4 | import BowLiteCore 5 | 6 | class WriterMonadTest: XCTestCase { 7 | func testMonadLaws() { 8 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 9 | let g = f.getArrow >>> Writer.pure 10 | 11 | return Writer.pure(a).flatMap(g) 12 | == 13 | g(a) 14 | } 15 | 16 | property("Monad right identity") <~ forAll { (a: Int) in 17 | let fa = Writer.pure(a) 18 | return fa.flatMap(Writer.pure) 19 | == 20 | fa 21 | } 22 | 23 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 24 | let g = f.getArrow >>> Writer.pure 25 | 26 | return ({ (n: Int) in Writer.pure(n) } >=> g)(a) 27 | == 28 | g(a) 29 | } 30 | 31 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 32 | let g = f.getArrow >>> Writer.pure 33 | 34 | return (g >=> Writer.pure)(a) 35 | == 36 | g(a) 37 | } 38 | 39 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 40 | let g = f.getArrow >>> Writer.pure 41 | let fa = Writer.pure(a) 42 | 43 | return fa.flatMap(g) 44 | == 45 | fa.map(f.getArrow) 46 | } 47 | 48 | property("Flatten") <~ forAll { (a: Int) in 49 | Writer>.pure(Writer.pure(a)).flatten() 50 | == 51 | Writer.pure(a) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Instances/BoolInstancesTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import BowLiteCore 3 | import BowLiteLaws 4 | 5 | class BoolInstancesTest: XCTestCase { 6 | 7 | func testBoolEqLaws() { 8 | EquatableLaws.check() 9 | } 10 | 11 | func testBoolSemiringLaws() { 12 | SemiringLaws.check() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Instances/StringInstancesTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import BowLiteCore 3 | import BowLiteLaws 4 | 5 | class StringInstancesTest: XCTestCase { 6 | 7 | func testEqLaws() { 8 | EquatableLaws.check() 9 | } 10 | 11 | func testSemigroupLaws() { 12 | SemigroupLaws.check() 13 | } 14 | 15 | func testMonoidLaws() { 16 | MonoidLaws.check() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Syntax/CompositionTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteCore 4 | import BowLiteLaws 5 | 6 | class CompostionTest : XCTestCase { 7 | 8 | func testComposition() { 9 | property("Function composition is equal to applying functions sequentially") <~ forAll() { (a : Int, g : ArrowOf) in 10 | let f = constant(a) 11 | let x1 = f() 12 | let x2 = g.getArrow(x1) 13 | return x2 == (g.getArrow <<< f)() 14 | } 15 | 16 | property("Function composition is equal to applying functions sequentially") <~ forAll() { (f : ArrowOf, g : ArrowOf, x1 : Int) in 17 | let x2 = f.getArrow(x1) 18 | let x3 = g.getArrow(x2) 19 | return x3 == (g.getArrow <<< f.getArrow)(x1) 20 | } 21 | 22 | property("Function composition is equal to applying functions sequentially") <~ forAll() { (a : Int, g : ArrowOf) in 23 | let f = constant(a) 24 | let x1 = f() 25 | let x2 = g.getArrow(x1) 26 | return x2 == compose(g.getArrow, f)() 27 | } 28 | 29 | property("Function composition is equal to applying functions sequentially") <~ forAll() { (f : ArrowOf, g : ArrowOf, x1 : Int) in 30 | let x2 = f.getArrow(x1) 31 | let x3 = g.getArrow(x2) 32 | return x3 == compose(g.getArrow, f.getArrow)(x1) 33 | } 34 | 35 | property("Function composition is associative") <~ forAll() { (f : ArrowOf, g : ArrowOf, h : ArrowOf, x : Int) in 36 | 37 | return ((h.getArrow <<< g.getArrow) <<< f.getArrow)(x) == (h.getArrow <<< (g.getArrow <<< f.getArrow))(x) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Syntax/CurryTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteCore 4 | import BowLiteLaws 5 | 6 | class CurryTest: XCTestCase { 7 | 8 | func testCurryingTwoArgumentFunctions() { 9 | func f(_ a : Int, _ b : Int) -> Int { 10 | return a + b 11 | } 12 | property("Curry and uncurry form an isomorphism") <~ forAll() { (a : Int, b : Int) in 13 | return uncurry(curry(f))(a, b) == f(a, b) 14 | } 15 | } 16 | 17 | func testCurryingThreeArgumentFunctions() { 18 | func f(_ a : Int, _ b : Int, _ c : Int) -> Int { 19 | return a + b + c 20 | } 21 | property("Curry and uncurry form an isomorphism") <~ forAll() { (a : Int, b : Int, c : Int) in 22 | return uncurry(curry(f))(a, b, c) == f(a, b, c) 23 | } 24 | } 25 | 26 | func testCurryingFourArgumentFunctions() { 27 | func f(_ a : Int, _ b : Int, _ c : Int, _ d : Int) -> Int { 28 | return a + b + c + d 29 | } 30 | property("Curry and uncurry form an isomorphism") <~ forAll() { (a : Int, b : Int, c : Int, d : Int) in 31 | return uncurry(curry(f))(a, b, c, d) == f(a, b, c, d) 32 | } 33 | } 34 | 35 | func testCurryingFiveArgumentFunctions() { 36 | func f(_ a : Int, _ b : Int, _ c : Int, _ d : Int, _ e : Int) -> Int { 37 | return a + b + c + d + e 38 | } 39 | property("Curry and uncurry form an isomorphism") <~ forAll() { (a : Int, b : Int, c : Int, d : Int, e : Int) in 40 | return uncurry(curry(f))(a, b, c, d, e) == f(a, b, c, d, e) 41 | } 42 | } 43 | 44 | func testCurryingSixArgumentFunctions() { 45 | func f(_ a : Int, _ b : Int, _ c : Int, _ d : Int, _ e : Int, _ g : Int) -> Int { 46 | return a + b + c + d + e + g 47 | } 48 | property("Curry and uncurry form an isomorphism") <~ forAll() { (a : Int, b : Int, c : Int, d : Int, e : Int, g : Int) in 49 | return uncurry(curry(f))(a, b, c, d, e, g) == f(a, b, c, d, e, g) 50 | } 51 | } 52 | 53 | func testCurryingSevenArgumentFunctions() { 54 | func f(_ a : Int, _ b : Int, _ c : Int, _ d : Int, _ e : Int, _ g : Int, _ h : Int) -> Int { 55 | return a + b + c + d + e + g + h 56 | } 57 | property("Curry and uncurry form an isomorphism") <~ forAll() { (a : Int, b : Int, c : Int, d : Int, e : Int, g : Int, h : Int) in 58 | return uncurry(curry(f))(a, b, c, d, e, g, h) == f(a, b, c, d, e, g, h) 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Tests/BowLiteCoreTests/Syntax/MemoizationTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | import BowLiteCore 4 | import BowLiteLaws 5 | 6 | class MemoizationTest: XCTestCase { 7 | 8 | let positiveInts = Int.arbitrary.suchThat { x in x > 0 } 9 | 10 | func testMemoizedFunctionCachesResults() { 11 | var timesCalled = 0 12 | func longRunningOperation(_ x : Int) -> Int { 13 | timesCalled += 1 14 | return x + 1 15 | } 16 | 17 | property("Memoized function is only called once for the same input") <~ forAll(Int.arbitrary, self.positiveInts) { (input : Int, times : Int) in 18 | timesCalled = 0 19 | let memoizedFunction = memoize(longRunningOperation) 20 | 21 | (0 ..< times).forEach { _ in let _ = memoizedFunction(input) } 22 | 23 | return timesCalled == 1 24 | } 25 | } 26 | 27 | let smallInts = Int.arbitrary.suchThat { x in x > 0 && x < 20 } 28 | 29 | func testMemoizedRecursiveFunctionCachesResult() { 30 | property("Memoized function is only called once for the same input") <~ forAll(self.smallInts, self.positiveInts) { (input : Int, times : Int) in 31 | var timesCalled = Dictionary() 32 | timesCalled[input] = 0 33 | 34 | let memoizedFactorial : (Int) -> Int = memoize { factorial, x in 35 | timesCalled[x] = (timesCalled[x] ?? 0) + 1 36 | return x == 0 ? 1 : x * factorial(x - 1) 37 | } 38 | 39 | (0 ..< times).forEach { _ in let _ = memoizedFactorial(input) } 40 | 41 | return timesCalled[input] == 1 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/EnvIO/EnvIO+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | class EnvIOApplicativeErrorTest: XCTestCase { 9 | func testApplicativeErrorLaws() { 10 | property("Applicative error handle") <~ forAll { (error: AnyError, f: ArrowOf) in 11 | 12 | EnvIO.raiseError(error).handleError(f.getArrow) 13 | == 14 | EnvIO.pure(f.getArrow(error)) 15 | } 16 | 17 | property("Applicative error handle with") <~ forAllNoShrink(envIOGen, AnyError.arbitrary) { (f: EnvIO, error: AnyError) in 18 | 19 | EnvIO.raiseError(error).handleErrorWith(constant(f)) 20 | == 21 | constant(f)(error) 22 | } 23 | 24 | property("Applicative error handle with pure") <~ forAllNoShrink(Int.arbitrary, envIOGen) { (a: Int, f: EnvIO) in 25 | 26 | EnvIO.pure(a).handleErrorWith(constant(f)) 27 | == 28 | EnvIO.pure(a) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/EnvIO/EnvIO+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | let envIOGen: Gen> = ioGen.map { io in EnvIO(constant(io)) } 9 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/EnvIO/EnvIO+Equatable.swift: -------------------------------------------------------------------------------- 1 | import BowLiteEffects 2 | 3 | extension EnvIO: Equatable where Dependencies == Int, Failure: Equatable, Success: Equatable { 4 | public static func ==(lhs: EnvIO, rhs: EnvIO) -> Bool { 5 | lhs.provide(0) == rhs.provide(0) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/EnvIO/EnvIO+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | class EnvIOFunctorTest: XCTestCase { 9 | func testFunctorLaws() { 10 | property("Identity is preserved under functor transformation") <~ forAllNoShrink(envIOGen) { (fa: EnvIO) in 11 | fa.map(id) 12 | == 13 | id(fa) 14 | } 15 | 16 | property("Composition is preserved under functor transformation") <~ forAllNoShrink(envIOGen, ArrowOf.arbitrary, ArrowOf.arbitrary) { (fa: EnvIO, f: ArrowOf, g: ArrowOf) in 17 | 18 | fa.map(f.getArrow).map(g.getArrow) 19 | == 20 | fa.map(f.getArrow >>> g.getArrow) 21 | } 22 | 23 | property("product") <~ forAllNoShrink(envIOGen, ArrowOf.arbitrary) { (fa: EnvIO, f: ArrowOf) in 24 | 25 | fa.product(f.getArrow).map { x in x.1 } 26 | == 27 | fa.map(f.getArrow) 28 | } 29 | 30 | property("tuple left") <~ forAllNoShrink(envIOGen, Int.arbitrary) { (fa: EnvIO, b: Int) in 31 | 32 | fa.tupleLeft(b).map { x in x.0 } 33 | == 34 | fa.as(b) 35 | } 36 | 37 | property("tuple right") <~ forAllNoShrink(envIOGen, Int.arbitrary) { (fa: EnvIO, b: Int) in 38 | 39 | fa.tupleRight(b).map { x in x.1 } 40 | == 41 | fa.as(b) 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/EnvIO/EnvIO+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | class EnvIOMonadTest: XCTestCase { 9 | func testMonadLaws() { 10 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 11 | let g = f.getArrow >>> EnvIO.pure 12 | 13 | return EnvIO.pure(a).flatMap(g) 14 | == 15 | g(a) 16 | } 17 | 18 | property("Monad right identity") <~ forAll { (a: Int) in 19 | let fa = EnvIO.pure(a) 20 | return fa.flatMap(EnvIO.pure) 21 | == 22 | fa 23 | } 24 | 25 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 26 | let g = f.getArrow >>> EnvIO.pure 27 | 28 | return ({ (n: Int) in EnvIO.pure(n) } >=> g)(a) 29 | == 30 | g(a) 31 | } 32 | 33 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 34 | let g = f.getArrow >>> EnvIO.pure 35 | 36 | return (g >=> EnvIO.pure)(a) 37 | == 38 | g(a) 39 | } 40 | 41 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 42 | let g = f.getArrow >>> EnvIO.pure 43 | let fa = EnvIO.pure(a) 44 | 45 | return fa.flatMap(g) 46 | == 47 | fa.map(f.getArrow) 48 | } 49 | 50 | property("Flatten") <~ forAll { (a: Int) in 51 | EnvIO>.pure(EnvIO.pure(a)).flatten() 52 | == 53 | EnvIO.pure(a) 54 | } 55 | 56 | property("Monad comprehensions equivalence to flatMap") <~ forAllNoShrink(envIOGen, envIOGen, envIOGen) { fa, fb, fc in 57 | let r1 = fa.flatMap { a in 58 | fb.flatMap { b in 59 | fc.map { c in "\(a), \(b), \(c)" } 60 | } 61 | } 62 | 63 | let x = EnvIO.var() 64 | let y = EnvIO.var() 65 | let z = EnvIO.var() 66 | 67 | let r2 = binding( 68 | x <-- fa, 69 | y <-- fb, 70 | z <-- fc, 71 | yield: "\(x.get), \(y.get), \(z.get)" 72 | ) 73 | 74 | return r1 == r2 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/IO/IO+ApplicativeErrorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | class IOApplicativeErrorTest: XCTestCase { 9 | func testApplicativeErrorLaws() { 10 | property("Applicative error handle") <~ forAll { (error: AnyError, f: ArrowOf) in 11 | 12 | IO.raiseError(error).handleError(f.getArrow) 13 | == 14 | IO.pure(f.getArrow(error)) 15 | } 16 | 17 | property("Applicative error handle with") <~ forAllNoShrink(ioGen, AnyError.arbitrary) { (f: IO, error: AnyError) in 18 | 19 | IO.raiseError(error).handleErrorWith(constant(f)) 20 | == 21 | constant(f)(error) 22 | } 23 | 24 | property("Applicative error handle with pure") <~ forAllNoShrink(Int.arbitrary, ioGen) { (a: Int, f: IO) in 25 | 26 | IO.pure(a).handleErrorWith(constant(f)) 27 | == 28 | IO.pure(a) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/IO/IO+Arbitrary.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | @testable import BowLiteEffects 7 | 8 | // IO cannot conform to Arbitrary as it is not a final class 9 | 10 | private let pure: Gen> = Int.arbitrary.map(Pure.init) 11 | 12 | private let raiseError: Gen> = AnyError.arbitrary.map(RaiseError.init) 13 | 14 | private let handleErrorWith: Gen> = 15 | Gen.zip(baseIOGen, baseIOGen).map { io, result in 16 | HandleErrorWith(io, constant(result)) 17 | } 18 | 19 | private let fmap: Gen> = Gen.zip(ArrowOf.arbitrary, baseIOGen).map { arrow, io in 20 | FMap(arrow.getArrow, io) 21 | } 22 | 23 | private let join: Gen> = baseIOGen.map(Pure.init).map(Join.init) 24 | 25 | private let asyncIO: Gen> = Gen.one(of: [ 26 | Int.arbitrary.map { value in 27 | AsyncIO { callback in 28 | callback(.right(value)) 29 | return IO.pure(()) 30 | } 31 | }, 32 | AnyError.arbitrary.map { error in 33 | AsyncIO { callback in 34 | callback(.left(error)) 35 | return IO.pure(()) 36 | } 37 | } 38 | ]) 39 | 40 | private let continueOn: Gen> = 41 | Gen.zip(baseIOGen, String.arbitrary.suchThat { str in !str.isEmpty }).map { io, label in 42 | ContinueOn(io, DispatchQueue(label: label)) 43 | } 44 | 45 | private let bracket: Gen> = Gen.zip(baseIOGen, baseIOGen, baseIOGen).map { acquire, release, use in 46 | BracketIO(acquire, constant(release.void()), constant(use)) 47 | } 48 | 49 | private let race: Gen> = baseIOGen.map { io in 50 | Race(io, IO.never()).map { either in either.merge() } 51 | } 52 | 53 | private let parMap2: Gen> = Gen.zip(baseIOGen, baseIOGen).map { a, b in 54 | ParMap2(a, b, +) 55 | } 56 | 57 | private let parMap3: Gen> = Gen.zip(baseIOGen, baseIOGen, baseIOGen).map { a, b, c in 58 | ParMap3(a, b, c) { x, y, z in x + y + z } 59 | } 60 | 61 | private let suspend: Gen> = baseIOGen.map { io in Suspend { io } } 62 | 63 | private let baseIOGen: Gen> = Gen.one(of: [pure, raiseError]) 64 | 65 | let ioGen: Gen> = Gen.one(of: [pure, raiseError, handleErrorWith, fmap, join, asyncIO, continueOn, bracket, race, parMap2, parMap3, suspend]) 66 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/IO/IO+Equatable.swift: -------------------------------------------------------------------------------- 1 | import BowLiteEffects 2 | 3 | extension IO: Equatable where Failure: Equatable, Success: Equatable { 4 | public static func ==(lhs: IO, rhs: IO) -> Bool { 5 | lhs.unsafeRunSyncEither() == rhs.unsafeRunSyncEither() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/IO/IO+FunctorTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | class IOFunctorTest: XCTestCase { 9 | func testFunctorLaws() { 10 | property("Identity is preserved under functor transformation") <~ forAllNoShrink(ioGen) { (fa: IO) in 11 | fa.map(id) 12 | == 13 | id(fa) 14 | } 15 | 16 | property("Composition is preserved under functor transformation") <~ forAllNoShrink(ioGen, ArrowOf.arbitrary, ArrowOf.arbitrary) { (fa: IO, f: ArrowOf, g: ArrowOf) in 17 | 18 | fa.map(f.getArrow).map(g.getArrow) 19 | == 20 | fa.map(f.getArrow >>> g.getArrow) 21 | } 22 | 23 | property("product") <~ forAllNoShrink(ioGen, ArrowOf.arbitrary) { (fa: IO, f: ArrowOf) in 24 | 25 | fa.product(f.getArrow).map { x in x.1 } 26 | == 27 | fa.map(f.getArrow) 28 | } 29 | 30 | property("tuple left") <~ forAllNoShrink(ioGen, Int.arbitrary) { (fa: IO, b: Int) in 31 | 32 | fa.tupleLeft(b).map { x in x.0 } 33 | == 34 | fa.as(b) 35 | } 36 | 37 | property("tuple right") <~ forAllNoShrink(ioGen, Int.arbitrary) { (fa: IO, b: Int) in 38 | 39 | fa.tupleRight(b).map { x in x.1 } 40 | == 41 | fa.as(b) 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Tests/BowLiteEffectsTests/IO/IO+MonadTest.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftCheck 3 | 4 | import BowLiteCore 5 | import BowLiteLaws 6 | import BowLiteEffects 7 | 8 | class IOMonadTest: XCTestCase { 9 | func testMonadLaws() { 10 | property("Monad left identity") <~ forAll { (a: Int, f: ArrowOf) in 11 | let g = f.getArrow >>> IO.pure 12 | 13 | return IO.pure(a).flatMap(g) 14 | == 15 | g(a) 16 | } 17 | 18 | property("Monad right identity") <~ forAll { (a: Int) in 19 | let fa = IO.pure(a) 20 | return fa.flatMap(IO.pure) 21 | == 22 | fa 23 | } 24 | 25 | property("Kleisli left identity") <~ forAll { (a: Int, f: ArrowOf) in 26 | let g = f.getArrow >>> IO.pure 27 | 28 | return ({ (n: Int) in IO.pure(n) } >=> g)(a) 29 | == 30 | g(a) 31 | } 32 | 33 | property("Kleisli right identity") <~ forAll { (a: Int, f: ArrowOf) in 34 | let g = f.getArrow >>> IO.pure 35 | 36 | return (g >=> IO.pure)(a) 37 | == 38 | g(a) 39 | } 40 | 41 | property("Monad flatMap coherence") <~ forAll { (a: Int, f: ArrowOf) in 42 | let g = f.getArrow >>> IO.pure 43 | let fa = IO.pure(a) 44 | 45 | return fa.flatMap(g) 46 | == 47 | fa.map(f.getArrow) 48 | } 49 | 50 | property("Flatten") <~ forAll { (a: Int) in 51 | IO>.pure(IO.pure(a)).flatten() 52 | == 53 | IO.pure(a) 54 | } 55 | 56 | property("Monad comprehensions equivalence to flatMap") <~ forAllNoShrink(ioGen, ioGen, ioGen) { fa, fb, fc in 57 | let r1 = fa.flatMap { a in 58 | fb.flatMap { b in 59 | fc.map { c in "\(a), \(b), \(c)" } 60 | } 61 | } 62 | 63 | let x = IO.var() 64 | let y = IO.var() 65 | let z = IO.var() 66 | 67 | let r2 = binding( 68 | x <-- fa, 69 | y <-- fb, 70 | z <-- fc, 71 | yield: "\(x.get), \(y.get), \(z.get)" 72 | ) 73 | 74 | return r1 == r2 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/AnyError.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | public enum AnyError: Error, Hashable { 5 | case randomFailure 6 | } 7 | 8 | extension AnyError: Arbitrary { 9 | public static var arbitrary: Gen { 10 | Gen.fromElements(of: [.randomFailure]) 11 | } 12 | } 13 | 14 | extension AnyError: CoArbitrary { 15 | public static func coarbitrary(_ x: AnyError) -> ((Gen) -> Gen) { 16 | id 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/CustomStringConvertibleLaws.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | public class CustomStringConvertibleLaws { 5 | public static func check(){ 6 | equality() 7 | } 8 | 9 | private static func equality() { 10 | property("Equal objects must show equal content") <~ forAll { (a: A) in 11 | let x1 = a 12 | let x2 = a 13 | return x1.description == x2.description 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/EquatableLaws.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | public class EquatableLaws { 5 | public static func check() { 6 | identity() 7 | commutativity() 8 | transitivity() 9 | } 10 | 11 | private static func identity() { 12 | property("Identity: Every object is equal to itself") <~ forAll { (a: A) in 13 | 14 | a == a 15 | } 16 | } 17 | 18 | private static func commutativity() { 19 | property("Equality is commutative") <~ forAll { (a: A, b: A) in 20 | 21 | (a == b) 22 | == 23 | (b == a) 24 | } 25 | } 26 | 27 | private static func transitivity() { 28 | property("Equality is transitive") <~ forAll { (a: A, b: A, c: A) in 29 | 30 | // (a == b) && (b == c) --> (a == c) 31 | not((a == b) && (b == c)) || (a == c) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/MonoidLaws.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | public class MonoidLaws { 5 | public static func check() { 6 | leftIdentity() 7 | rightIdentity() 8 | } 9 | 10 | private static func leftIdentity() { 11 | property("Left identity") <~ forAll { (a: A) in 12 | A.empty.combine(a) == a 13 | } 14 | } 15 | 16 | private static func rightIdentity() { 17 | property("Right identity") <~ forAll { (a: A) in 18 | a.combine(A.empty) == a 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/PropertyOperatorOverload.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | infix operator <~ 5 | 6 | public func <~(checker: AssertiveQuickCheck, test: @autoclosure @escaping () -> Testable) { 7 | checker <- test 8 | } 9 | 10 | public func <~(checker: AssertiveQuickCheck, test: () -> Testable) { 11 | checker <- test 12 | } 13 | 14 | public func <~(checker: ReportiveQuickCheck, test: () -> Testable) { 15 | checker <- test 16 | } 17 | 18 | public func <~(checker: ReportiveQuickCheck, test: @autoclosure @escaping () -> Testable) { 19 | checker <~ test 20 | } 21 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/SemigroupLaws.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | public class SemigroupLaws { 5 | public static func check() { 6 | associativity() 7 | reduction() 8 | } 9 | 10 | private static func associativity() { 11 | property("Associativity") <~ forAll { (a: A, b: A, c: A) in 12 | a.combine(b).combine(c) 13 | == 14 | a.combine(b.combine(c)) 15 | } 16 | } 17 | 18 | private static func reduction() { 19 | property("Reduction") <~ forAll { (a: A, b: A, c: A) in 20 | A.combineAll(a, b, c) 21 | == 22 | a.combine(b).combine(c) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/BowLiteLaws/SemiringLaws.swift: -------------------------------------------------------------------------------- 1 | import SwiftCheck 2 | import BowLiteCore 3 | 4 | public class SemiringLaws { 5 | public static func check() { 6 | MonoidLaws.check() 7 | commutativityForCombining() 8 | associativityForMultiplying() 9 | leftIdentityForMultiplying() 10 | rightIdentityForMultiplying() 11 | leftDistribution() 12 | rightDistribution() 13 | leftAnnihilation() 14 | rightAnnihilation() 15 | } 16 | 17 | private static func commutativityForCombining() { 18 | property("Commutativity for combining") <~ forAll { (a: A) in 19 | A.zero.combine(a) 20 | == 21 | a.combine(.zero) 22 | } 23 | } 24 | 25 | private static func associativityForMultiplying() { 26 | property("Ascociativity for multiplying") <~ forAll { (a: A, b: A, c: A) in 27 | a.multiply(b).multiply(c) 28 | == 29 | a.multiply(b.multiply(c)) 30 | } 31 | } 32 | 33 | private static func leftIdentityForMultiplying() { 34 | property("Left identity for multiplying") <~ forAll { (a: A) in 35 | A.one.multiply(a) == a 36 | } 37 | } 38 | 39 | private static func rightIdentityForMultiplying() { 40 | property("Right identity for multiplying") <~ forAll { (a: A) in 41 | a.multiply(.one) == a 42 | } 43 | } 44 | 45 | private static func leftDistribution() { 46 | property("Left distribution") <~ forAll { (a: A, b: A, c: A) in 47 | a.multiply(b.combine(c)) 48 | == 49 | (a.multiply(b)).combine(a.multiply(c)) 50 | } 51 | } 52 | 53 | private static func rightDistribution() { 54 | property("Right distribution") <~ forAll { (a: A, b: A, c: A) in 55 | a.combine(b).multiply(c) 56 | == 57 | a.multiply(c).combine(b.multiply(c)) 58 | } 59 | } 60 | 61 | private static func leftAnnihilation() { 62 | property("Left Annihilation") <~ forAll { (a: A) in 63 | A.zero.multiply(a) == .zero 64 | } 65 | } 66 | 67 | private static func rightAnnihilation() { 68 | property("Right Annihilation") <~ forAll { (a: A) in 69 | a.multiply(.zero) == .zero 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /assets/bow-lite-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bow-swift/bow-lite/520042b1fdc1c09855a5eb8cafff3b9a791952b6/assets/bow-lite-banner.png --------------------------------------------------------------------------------