Skip to content

Commit

Permalink
Merge pull request #40 from broomburgo/master
Browse files Browse the repository at this point in the history
Add Travis CI
  • Loading branch information
broomburgo authored May 23, 2018
2 parents dfca21e + 1cea8d8 commit 77f2902
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 16 deletions.
30 changes: 30 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
env:
global:
- LC_CTYPE=en_US.UTF-8
matrix:
include:
- os: osx
language: objective-c
osx_image: xcode9.3
before_install:
- git submodule update --init --recursive
- carthage build --no-skip-current
script:
- set -o pipefail
- xcodebuild test -scheme Abstract | xcpretty -c
- xcodebuild build-for-testing -scheme Abstract-iOS -destination "platform=iOS Simulator,name=iPad Pro (12.9-inch) (2nd generation)" | xcpretty -c
- xcodebuild test -scheme Abstract-iOS -destination "platform=iOS Simulator,name=iPad Pro (12.9-inch) (2nd generation)" | xcpretty -c
- xcodebuild build-for-testing -scheme Abstract-tvOS -destination 'platform=tvOS Simulator,name=Apple TV 4K (at 1080p)' | xcpretty -c
- xcodebuild test -scheme Abstract-tvOS -destination 'platform=tvOS Simulator,name=Apple TV 4K (at 1080p)' | xcpretty -c
- os: linux
language: generic
sudo: required
dist: trusty
before_install:
- git submodule update --init --recursive
- wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
- wget https://swift.org/builds/swift-4.1-release/ubuntu1404/swift-4.1-RELEASE/swift-4.1-RELEASE-ubuntu14.04.tar.gz
- tar xzf swift-4.1-RELEASE-ubuntu14.04.tar.gz
- export PATH=${PWD}/swift-4.1-RELEASE-ubuntu14.04/usr/bin:"${PATH}"
script:
- swift test
4 changes: 4 additions & 0 deletions Abstract.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@
0951F2431F09690200EA362C /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/Mac/SwiftCheck.framework; sourceTree = "<group>"; };
0951F2441F09690200EA362C /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/tvOS/SwiftCheck.framework; sourceTree = "<group>"; };
09F9FD0E1EEE5CE8002342E5 /* Homomorphism.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Homomorphism.swift; sourceTree = "<group>"; };
3806599920AAB6360085187D /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = "<group>"; };
3806599A20AAB6530085187D /* RATIONALE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RATIONALE.md; sourceTree = "<group>"; };
380A4B141F1B7336002D70E0 /* SemigroupTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemigroupTests.generated.swift; sourceTree = "<group>"; };
380A4B191F1B775F002D70E0 /* WrapperTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrapperTests.generated.swift; sourceTree = "<group>"; };
380A4B1F1F1BE383002D70E0 /* MonoidTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonoidTests.generated.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -526,10 +528,12 @@
children = (
38757E101F4D85AB0059AD71 /* Templates */,
38D737DF1EE9A745000BAF0C /* Abstract.h */,
3806599A20AAB6530085187D /* RATIONALE.md */,
38B83C9D1EED8D11007AB12A /* README.md */,
38D737E01EE9A745000BAF0C /* Info.plist */,
38D737E11EE9A761000BAF0C /* InfoTests.plist */,
38D737BB1EE9A711000BAF0C /* Package.swift */,
3806599920AAB6360085187D /* .travis.yml */,
38943A6A1EEC287000F587AD /* Frameworks */,
38D737C81EE9A729000BAF0C /* Products */,
38D737BC1EE9A711000BAF0C /* Sources */,
Expand Down
4 changes: 2 additions & 2 deletions RATIONALE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ How can we be sure that a certain type is a *proper* `BoundedSemilattice`? In fa

One of the main points of the `Abstract` library is providing the user with simple means to test concrete types against the laws that the type must respect.

The `Law` and `LawInContext` namespaces provide some functions for types for which the `<>` operation is supposed to respect some laws; these functions require that the tested type is `Equatable` because the `==` operation is used to check if the equations that express the law evaluate into `true` or `false`. Because at the present moment there's no possibility of conditional conformance to the `Equatable` protocol in Swift's type system, all the concrete types in `Abstract` are made to conform either to `Equatable` or `EquatableInContext` (the latter is used for types that wrap functions). When conditional conformance [will be implemented](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md) into Swift, there will be a `Equatable` implementation for the various types only under certain assumptions.
The `Law` and `LawInContext` namespaces provide some functions for types for which the `<>` operation is supposed to respect some laws; these functions require that the tested type is `Equatable` because the `==` operation is used to check if the equations that express the law evaluate into `true` or `false`.

------

Expand Down Expand Up @@ -108,4 +108,4 @@ Mistakes are bad, and should be corrected. But I think it's extremely important

A plain translation into Swift from a language with a more sophisticated type system will almost certainly result in something ugly and impractical. On the other hand, I'm also interested in pushing the boundaries of Swift's expressivity, thus I think it's ok to do stuff a little less rigorous (without losing laws conformance or writing unsound code, of course) but much more practically usable.

I hope that this library will inspire people to find new and interesting ways to take advantage of the basic theoretical concepts of abstract algebra in their day-to-day Swift code, while also trying to push the boundaries of Swift, thus contributing to the general improvement of the language in the process.
I hope that this library will inspire people to find new and interesting ways to take advantage of the basic theoretical concepts of abstract algebra in their day-to-day Swift code, while also trying to push the boundaries of Swift, thus contributing to the general improvement of the language in the process.
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Abstract

A take on abstract algebraic structures, in Swift.
A take on abstract algebraic structures, in Swift.

------

Expand Down Expand Up @@ -237,10 +237,10 @@ Notice that we could call `.reduce(.empty, <>)` on **any** collection were the e
- can be composed with `<>`;
- have an empty element;

Thus, if we were able to represent these two properties in an abstract way, we could simply define a `.concatenated` method for these kinds of collections:
Thus, if we were able to represent these two properties in an abstract way, we could simply define a `.concatenated()` method for these kinds of collections:

```swift
let finalSession = sessions.concatenated
let finalSession = sessions.concatenated()
```

A type (actually a set, but in programming we really just care about types) *equipped* with a composition operation that is *closed* (i.e. non-crashing) and *associative*, and an `.empty` value that is neutral to the composition, is usually called a `Monoid`: all the types defined in this example are monoids, and the Swift type system is strong enough to generically define the interface of a monoid with a `protocol`. Most of the types and methods used in this example are already defined in `Abstract`, and to read more about monoids you can refer to the [Monoid.swift](Sources/Abstract/Monoid.swift) source file.
Expand All @@ -260,19 +260,19 @@ Let's call "special divisors" the numbers associated to each word (initially, 3

The first composition style is simple concatenation; the second one is a little harder to see as some kind of composition, but it actually is the composition where we get only the first value if it exists (even if both exist), otherwise we get the second, and if none exist we get an "empty" value.

The type representing the string concatenation is simply `String`, which naturally forms a monoid over concatenation, where the `.empty` value is just the empty string. For the second type of composition we need a special type, that in `Abstract` is called `FirstM`: in composition, it will give priority to the first value.
The type representing the string concatenation is simply `String`, which naturally forms a monoid over concatenation, where the `.empty` value is just the empty string.

About the simple string concatenation, we'd like to define a function that *associates* a *word* to a special divisor: the function will take an `Int` and return a `String`, which is going to be "Fizz" or "Buzz". But instead of concatenating words we would actually like to concatenate *functions* that return words: if we're able to compose the return value, we can actually define a *composable function*:

```swift
func associate(divisor: Int, to text: String) -> FunctionM<Int,String> {
return FunctionM.init { value in
func associate(divisor: Int, to text: String) -> Function<Int,String> {
return Function.init { value in
value % divisor == 0 ? text : .empty
}
}
```

The `FunctionM` type is a *function type* (we get the function back with the `.call` method) that's **also** a monoid, so we can compose and concatenate instances of this function like we'd do for `String` values.
The `Function` type is a *function type* (we get the function back with the `.call` method) that's **also** a monoid, so we can compose and concatenate instances of this function like we'd do for `String` values.

We can easily define our `fizz` and `buzz` associations:

Expand All @@ -284,14 +284,19 @@ let buzz = associate(divisor: 5, to: "Buzz")
Now we can easily generate a function that will transform a number in a word, properly concatenated (like "FizzBuzz" for the number 15), or an empty string if the number has no special divisor.

```swift
let transform = [fizz, buzz].concatenated.call
let transform = [fizz, buzz].concatenated().call
```

Finally, we need a second kind of composition: the one in which the first value is selected if it's not `.empty`. The `FirstM` type has exactly this semantics. We can define a `getWord` function that will use `FirstM` to select a value in a composition:
For the second type of composition, Swift already provides a type with the correct semantics; we need to give priority to the *first* element, but only if it's not `.empty`, otherwise we yield the second value (`.empty` or not): that's exactly the composition semantics of `Optional`, where `.empty` is `.none` (or `nil`) and the composition operation is represented by the `??` operator. `Abstract` extends `Optional` with the `Monoid` protocol, adding the `.empty` instance and the `<>` operator. We can define a `getWord` function that will use `Optional<String>` to select a value in a composition:

```swift
func getWord<T>(for value: T, with association: @escaping (T) -> String) -> FirstM<String> {
return FirstM(association(value)) <> FirstM("\(value)")
func getWord<T>(for value: T, with association: @escaping (T) -> String) -> String {
let optionalAssociated = Optional(association(value))
.flatMap { $0 == .empty ? Optional.empty : Optional($0) }

let optionalValue = Optional("\(value)")

return (optionalAssociated <> optionalValue) ?? ""
}
```

Expand All @@ -302,7 +307,7 @@ let result = (1...100)
.map { value in
getWord(for: value, with: transform).unwrap <> "\n"
}
.concatenated
.concatenated()

print(result)
```
Expand All @@ -311,7 +316,7 @@ Now that we've separated the two kinds of composition that are taking place here

```swift
let bazz = associate(divisor: 4, to: "Bazz")
let transform = [fizz, buzz, bazz].concatenated.call
let transform = [fizz, buzz, bazz].concatenated().call
```

This code will add the word "Bazz" to the mix, for all numbers divisible by 4. Notice that in our example, for the number 60 the word "FizzBuzzBazz" will be printed: the order matters here, and we get "Bazz" at the end because we composed our transformation like `[fizz, buzz, bazz]`.
Expand Down
2 changes: 1 addition & 1 deletion Tests/AbstractTests/MultisetTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ final class MultisetTests: XCTestCase {
func testAlgebraSubsetSuperset() {
property("A multiset with a sample of elements from another is subset of another") <- forAll { (a: Multiset<Int>) in
var sample = Multiset<Int>()
let maxCount = arc4random_uniform(UInt32(a.count))
let maxCount = Gen.fromElements(in: 0...a.count).generate

for item in a.allItems where sample.count < maxCount {
sample.add(item)
Expand Down

0 comments on commit 77f2902

Please sign in to comment.