Skip to content

Commit 77f2902

Browse files
authored
Merge pull request #40 from broomburgo/master
Add Travis CI
2 parents dfca21e + 1cea8d8 commit 77f2902

File tree

5 files changed

+55
-16
lines changed

5 files changed

+55
-16
lines changed

.travis.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
env:
2+
global:
3+
- LC_CTYPE=en_US.UTF-8
4+
matrix:
5+
include:
6+
- os: osx
7+
language: objective-c
8+
osx_image: xcode9.3
9+
before_install:
10+
- git submodule update --init --recursive
11+
- carthage build --no-skip-current
12+
script:
13+
- set -o pipefail
14+
- xcodebuild test -scheme Abstract | xcpretty -c
15+
- xcodebuild build-for-testing -scheme Abstract-iOS -destination "platform=iOS Simulator,name=iPad Pro (12.9-inch) (2nd generation)" | xcpretty -c
16+
- xcodebuild test -scheme Abstract-iOS -destination "platform=iOS Simulator,name=iPad Pro (12.9-inch) (2nd generation)" | xcpretty -c
17+
- xcodebuild build-for-testing -scheme Abstract-tvOS -destination 'platform=tvOS Simulator,name=Apple TV 4K (at 1080p)' | xcpretty -c
18+
- xcodebuild test -scheme Abstract-tvOS -destination 'platform=tvOS Simulator,name=Apple TV 4K (at 1080p)' | xcpretty -c
19+
- os: linux
20+
language: generic
21+
sudo: required
22+
dist: trusty
23+
before_install:
24+
- git submodule update --init --recursive
25+
- wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
26+
- wget https://swift.org/builds/swift-4.1-release/ubuntu1404/swift-4.1-RELEASE/swift-4.1-RELEASE-ubuntu14.04.tar.gz
27+
- tar xzf swift-4.1-RELEASE-ubuntu14.04.tar.gz
28+
- export PATH=${PWD}/swift-4.1-RELEASE-ubuntu14.04/usr/bin:"${PATH}"
29+
script:
30+
- swift test

Abstract.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@
276276
0951F2431F09690200EA362C /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/Mac/SwiftCheck.framework; sourceTree = "<group>"; };
277277
0951F2441F09690200EA362C /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftCheck.framework; path = Carthage/Build/tvOS/SwiftCheck.framework; sourceTree = "<group>"; };
278278
09F9FD0E1EEE5CE8002342E5 /* Homomorphism.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Homomorphism.swift; sourceTree = "<group>"; };
279+
3806599920AAB6360085187D /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = "<group>"; };
280+
3806599A20AAB6530085187D /* RATIONALE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RATIONALE.md; sourceTree = "<group>"; };
279281
380A4B141F1B7336002D70E0 /* SemigroupTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemigroupTests.generated.swift; sourceTree = "<group>"; };
280282
380A4B191F1B775F002D70E0 /* WrapperTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WrapperTests.generated.swift; sourceTree = "<group>"; };
281283
380A4B1F1F1BE383002D70E0 /* MonoidTests.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonoidTests.generated.swift; sourceTree = "<group>"; };
@@ -526,10 +528,12 @@
526528
children = (
527529
38757E101F4D85AB0059AD71 /* Templates */,
528530
38D737DF1EE9A745000BAF0C /* Abstract.h */,
531+
3806599A20AAB6530085187D /* RATIONALE.md */,
529532
38B83C9D1EED8D11007AB12A /* README.md */,
530533
38D737E01EE9A745000BAF0C /* Info.plist */,
531534
38D737E11EE9A761000BAF0C /* InfoTests.plist */,
532535
38D737BB1EE9A711000BAF0C /* Package.swift */,
536+
3806599920AAB6360085187D /* .travis.yml */,
533537
38943A6A1EEC287000F587AD /* Frameworks */,
534538
38D737C81EE9A729000BAF0C /* Products */,
535539
38D737BC1EE9A711000BAF0C /* Sources */,

RATIONALE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ How can we be sure that a certain type is a *proper* `BoundedSemilattice`? In fa
7777

7878
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.
7979

80-
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.
80+
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`.
8181

8282
------
8383

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

109109
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.
110110

111-
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.
111+
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.

README.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Abstract
22

3-
A take on abstract algebraic structures, in Swift.
3+
A take on abstract algebraic structures, in Swift.
44

55
------
66

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

240-
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:
240+
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:
241241

242242
```swift
243-
let finalSession = sessions.concatenated
243+
let finalSession = sessions.concatenated()
244244
```
245245

246246
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.
@@ -260,19 +260,19 @@ Let's call "special divisors" the numbers associated to each word (initially, 3
260260

261261
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.
262262

263-
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.
263+
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.
264264

265265
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*:
266266

267267
```swift
268-
func associate(divisor: Int, to text: String) -> FunctionM<Int,String> {
269-
return FunctionM.init { value in
268+
func associate(divisor: Int, to text: String) -> Function<Int,String> {
269+
return Function.init { value in
270270
value % divisor == 0 ? text : .empty
271271
}
272272
}
273273
```
274274

275-
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.
275+
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.
276276

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

@@ -284,14 +284,19 @@ let buzz = associate(divisor: 5, to: "Buzz")
284284
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.
285285

286286
```swift
287-
let transform = [fizz, buzz].concatenated.call
287+
let transform = [fizz, buzz].concatenated().call
288288
```
289289

290-
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:
290+
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:
291291

292292
```swift
293-
func getWord<T>(for value: T, with association: @escaping (T) -> String) -> FirstM<String> {
294-
return FirstM(association(value)) <> FirstM("\(value)")
293+
func getWord<T>(for value: T, with association: @escaping (T) -> String) -> String {
294+
let optionalAssociated = Optional(association(value))
295+
.flatMap { $0 == .empty ? Optional.empty : Optional($0) }
296+
297+
let optionalValue = Optional("\(value)")
298+
299+
return (optionalAssociated <> optionalValue) ?? ""
295300
}
296301
```
297302

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

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

312317
```swift
313318
let bazz = associate(divisor: 4, to: "Bazz")
314-
let transform = [fizz, buzz, bazz].concatenated.call
319+
let transform = [fizz, buzz, bazz].concatenated().call
315320
```
316321

317322
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]`.

Tests/AbstractTests/MultisetTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ final class MultisetTests: XCTestCase {
77
func testAlgebraSubsetSuperset() {
88
property("A multiset with a sample of elements from another is subset of another") <- forAll { (a: Multiset<Int>) in
99
var sample = Multiset<Int>()
10-
let maxCount = arc4random_uniform(UInt32(a.count))
10+
let maxCount = Gen.fromElements(in: 0...a.count).generate
1111

1212
for item in a.allItems where sample.count < maxCount {
1313
sample.add(item)

0 commit comments

Comments
 (0)