diff --git a/.gitmodules b/.gitmodules
index 70cf4a63..efd56c80 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,20 +2,23 @@
path = xyzw-templating-languages/Vendor/Stencil
url = https://github.com/stencilproject/Stencil.git
[submodule "0017-styling-pt2/Vendor/swift-overture"]
- path = 0017-styling-pt2/Vendor/swift-overture
- url = https://github.com/pointfreeco/swift-overture.git
+ path = 0017-styling-pt2/Vendor/swift-overture
+ url = https://github.com/pointfreeco/swift-overture.git
[submodule "0018-environment-pt2/Vendor/swift-overture"]
- path = 0018-environment-pt2/Vendor/swift-overture
- url = https://github.com/pointfreeco/swift-overture.git
+ path = 0018-environment-pt2/Vendor/swift-overture
+ url = https://github.com/pointfreeco/swift-overture.git
[submodule "0021-playground-driven-development/Vendor/swift-overture"]
- path = 0021-playground-driven-development/Vendor/swift-overture
- url = https://github.com/pointfreeco/swift-overture.git
+ path = 0021-playground-driven-development/Vendor/swift-overture
+ url = https://github.com/pointfreeco/swift-overture.git
[submodule "0024-zip-pt2/Vendor/swift-nonempty"]
- path = 0024-zip-pt2/Vendor/swift-nonempty
- url = https://github.com/pointfreeco/swift-nonempty.git
+ path = 0024-zip-pt2/Vendor/swift-nonempty
+ url = https://github.com/pointfreeco/swift-nonempty.git
[submodule "0025-zip-pt3/Vendor/swift-nonempty"]
- path = 0025-zip-pt3/Vendor/swift-nonempty
- url = https://github.com/pointfreeco/swift-nonempty.git
-[submodule "0031-arbitrary/Vendor/Tagged"]
+ path = 0025-zip-pt3/Vendor/swift-nonempty
+ url = https://github.com/pointfreeco/swift-nonempty.git
+[submodule "0031-arbitrary-pt1/Vendor/Tagged"]
path = 0031-arbitrary-pt1/Vendor/Tagged
url = https://github.com/pointfreeco/swift-tagged.git
+[submodule "0032-arbitrary-pt2/Vendor/Tagged"]
+ path = 0032-arbitrary-pt2/Vendor/Tagged
+ url = https://github.com/pointfreeco/swift-tagged.git
diff --git a/0031-arbitrary-pt1/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift b/0031-arbitrary-pt1/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift
index 20fa4686..fd3a7c8f 100644
--- a/0031-arbitrary-pt1/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift
+++ b/0031-arbitrary-pt1/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift
@@ -1,5 +1,5 @@
/*:
- # Decodable Randomness
+ # Decodable Randomness: Part 1
## Exercises
diff --git a/0031-arbitrary-pt1/Arbitrary.playground/contents.xcplayground b/0031-arbitrary-pt1/Arbitrary.playground/contents.xcplayground
index 6dd3774b..d4a13862 100644
--- a/0031-arbitrary-pt1/Arbitrary.playground/contents.xcplayground
+++ b/0031-arbitrary-pt1/Arbitrary.playground/contents.xcplayground
@@ -1,7 +1,2 @@
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/0031-arbitrary-pt1/README.md b/0031-arbitrary-pt1/README.md
index bc46e3d5..3e1bd109 100644
--- a/0031-arbitrary-pt1/README.md
+++ b/0031-arbitrary-pt1/README.md
@@ -1,5 +1,5 @@
## [Point-Free](https://www.pointfree.co)
-> #### This directory contains code from Point-Free Episode: [Arbitrary TODO](https://www.pointfree.co/episodes/ep31-arbitrary-TODO)
+> #### This directory contains code from Point-Free Episode: [Decodable Randomness: Part 1](https://www.pointfree.co/episodes/ep31-decodable-randomness-part-1)
>
-> TODO
+> This week we dive deeper into randomness and composition by looking to a seemingly random place: the `Decodable` protocol. While we’re used to using the `Codable` set of protocols when working with JSON serialization and deserialization, it opens the opportunity for so much more.
diff --git a/0032-arbitrary-pt2/Arbitrary.playground/Pages/01-episode.xcplaygroundpage/Contents.swift b/0032-arbitrary-pt2/Arbitrary.playground/Pages/01-episode.xcplaygroundpage/Contents.swift
new file mode 100644
index 00000000..103eeca2
--- /dev/null
+++ b/0032-arbitrary-pt2/Arbitrary.playground/Pages/01-episode.xcplaygroundpage/Contents.swift
@@ -0,0 +1,335 @@
+
+//Decodable
+
+import Foundation
+//JSONDecoder().decode(<#T##type: Decodable.Protocol##Decodable.Protocol#>, from: <#T##Data#>)
+
+struct ArbitraryDecoder: Decoder {
+ var codingPath: [CodingKey] = []
+ var userInfo: [CodingUserInfoKey: Any] = [:]
+
+ func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey {
+
+ return KeyedDecodingContainer(KeyedContainer())
+ }
+
+ struct KeyedContainer: KeyedDecodingContainerProtocol {
+ var codingPath: [CodingKey] = []
+ var allKeys: [Key] = []
+
+ func contains(_ key: Key) -> Bool {
+ fatalError()
+ }
+
+ func decodeNil(forKey key: Key) throws -> Bool {
+ fatalError()
+ }
+
+ func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
+ return try T(from: ArbitraryDecoder())
+ }
+
+ func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer where NestedKey : CodingKey {
+ fatalError()
+ }
+
+ func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
+ fatalError()
+ }
+
+ func superDecoder() throws -> Decoder {
+ fatalError()
+ }
+
+ func superDecoder(forKey key: Key) throws -> Decoder {
+ fatalError()
+ }
+
+
+
+
+ }
+
+ func unkeyedContainer() throws -> UnkeyedDecodingContainer {
+ fatalError()
+ }
+
+ func singleValueContainer() throws -> SingleValueDecodingContainer {
+ return SingleValueContainer()
+ }
+
+ struct SingleValueContainer: SingleValueDecodingContainer {
+ var codingPath: [CodingKey] = []
+
+ func decodeNil() -> Bool {
+ return .random()
+ }
+
+ func decode(_ type: Bool.Type) throws -> Bool {
+ return .random()
+ }
+
+ func decode(_ type: String.Type) throws -> String {
+ return Array(repeating: (), count: .random(in: 0...280))
+ .map { String(UnicodeScalar(UInt8.random(in: .min ... .max))) }
+ .joined()
+ }
+
+ func decode(_ type: Double.Type) throws -> Double {
+ return .random(in: -1_000_000_000...1_000_000_000)
+ }
+
+ func decode(_ type: Float.Type) throws -> Float {
+ return .random(in: 0...1)
+ }
+
+ func decode(_ type: Int.Type) throws -> Int {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: Int8.Type) throws -> Int8 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: Int16.Type) throws -> Int16 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: Int32.Type) throws -> Int32 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: Int64.Type) throws -> Int64 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: UInt.Type) throws -> UInt {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: UInt8.Type) throws -> UInt8 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: UInt16.Type) throws -> UInt16 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: UInt32.Type) throws -> UInt32 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: UInt64.Type) throws -> UInt64 {
+ return .random(in: .min ... .max)
+ }
+
+ func decode(_ type: T.Type) throws -> T where T: Decodable {
+ return try T(from: ArbitraryDecoder())
+ }
+ }
+}
+
+try Bool(from: ArbitraryDecoder())
+try Bool(from: ArbitraryDecoder())
+try Bool(from: ArbitraryDecoder())
+try Bool(from: ArbitraryDecoder())
+try Bool(from: ArbitraryDecoder())
+try Int(from: ArbitraryDecoder())
+try Int(from: ArbitraryDecoder())
+try Int(from: ArbitraryDecoder())
+try Int(from: ArbitraryDecoder())
+try Int(from: ArbitraryDecoder())
+try UInt8(from: ArbitraryDecoder())
+try UInt8(from: ArbitraryDecoder())
+try UInt8(from: ArbitraryDecoder())
+try UInt8(from: ArbitraryDecoder())
+try UInt8(from: ArbitraryDecoder())
+try Double(from: ArbitraryDecoder())
+try Double(from: ArbitraryDecoder())
+try Double(from: ArbitraryDecoder())
+try Double(from: ArbitraryDecoder())
+try Double(from: ArbitraryDecoder())
+try Float(from: ArbitraryDecoder())
+try Float(from: ArbitraryDecoder())
+try Float(from: ArbitraryDecoder())
+try Float(from: ArbitraryDecoder())
+try Float(from: ArbitraryDecoder())
+try String(from: ArbitraryDecoder())
+try String(from: ArbitraryDecoder())
+try String(from: ArbitraryDecoder())
+try String(from: ArbitraryDecoder())
+try String(from: ArbitraryDecoder())
+try String?(from: ArbitraryDecoder())
+try String?(from: ArbitraryDecoder())
+try String?(from: ArbitraryDecoder())
+try String?(from: ArbitraryDecoder())
+try String?(from: ArbitraryDecoder())
+try Date(from: ArbitraryDecoder())
+try Date(from: ArbitraryDecoder())
+try Date(from: ArbitraryDecoder())
+try Date(from: ArbitraryDecoder())
+try Date(from: ArbitraryDecoder())
+//try UUID(from: ArbitraryDecoder())
+
+import Tagged
+
+struct User: Decodable {
+ typealias Id = Tagged
+
+ let id: Id
+ let name: String
+ let email: String
+}
+
+//print(try User(from: ArbitraryDecoder()))
+//print(try User(from: ArbitraryDecoder()))
+//print(try User(from: ArbitraryDecoder()))
+//print(try User(from: ArbitraryDecoder()))
+
+
+struct Gen {
+ let run: () -> A
+}
+
+import Darwin
+let random = Gen(run: arc4random)
+
+extension Gen {
+ func map(_ f: @escaping (A) -> B) -> Gen {
+ return Gen { f(self.run()) }
+ }
+}
+
+let uint64 = Gen {
+ let lower = UInt64(random.run())
+ let upper = UInt64(random.run()) << 32
+ return lower + upper
+}
+
+func int(in range: ClosedRange) -> Gen {
+ return Gen {
+ var delta = UInt64(truncatingIfNeeded: range.upperBound &- range.lowerBound)
+ if delta == UInt64.max {
+ return Int(truncatingIfNeeded: uint64.run())
+ }
+ delta += 1
+ let tmp = UInt64.max % delta + 1
+ let upperBound = tmp == delta ? 0 : tmp
+ var random: UInt64 = 0
+ repeat {
+ random = uint64.run()
+ } while random < upperBound
+ return Int(
+ truncatingIfNeeded: UInt64(truncatingIfNeeded: range.lowerBound)
+ &+ random % delta
+ )
+ }
+}
+
+func element(of xs: [A]) -> Gen {
+ return int(in: 0...(xs.count - 1)).map { idx in
+ guard !xs.isEmpty else { return nil }
+ return xs[idx]
+ }
+}
+
+extension Gen {
+ func array(count: Gen) -> Gen<[A]> {
+ return Gen<[A]> {
+ Array(repeating: (), count: count.run())
+ .map { self.run() }
+ }
+ }
+}
+
+func uint8(in range: ClosedRange) -> Gen {
+ return int(in: Int(UInt8.min)...Int(UInt8.max))
+ .map(UInt8.init)
+}
+
+let string = uint8(in: .min ... .max)
+// .map { String(UnicodeScalar($0)) }
+// .map(UnicodeScalar.init)
+// .map(String.init)
+ .map(UnicodeScalar.init >>> String.init)
+ .array(count: int(in: 0...280))
+ .map { $0.joined() }
+
+string.run()
+string.run()
+string.run()
+
+"DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF"
+
+
+extension Gen where A == Character {
+ func string(count: Gen) -> Gen {
+ return self.map(String.init).array(count: count).map { $0.joined() }
+ }
+}
+
+let hex = element(of: Array("0123456789ABCDEF")).map { $0! }
+let uuidString = Gen {
+ hex.string(count: .init { 8 }).run()
+ + "-" + hex.string(count: .init { 4 }).run()
+ + "-" + hex.string(count: .init { 4 }).run()
+ + "-" + hex.string(count: .init { 4 }).run()
+ + "-" + hex.string(count: .init { 12 }).run()
+}
+let randomUuid = uuidString.map(UUID.init).map { $0! }
+
+User(
+ id: randomUuid.map(User.Id.init).run(),
+ name: string.run(),
+ email: string.run()
+)
+
+
+let alpha = element(of: Array("abcdefghijklmnopqrstuvwxyz")).map { $0! }
+let namePart = alpha.string(count: int(in: 4...8))
+let capitalNamePart = namePart.map { $0.capitalized }
+let randomName = Gen { capitalNamePart.run() + " " + capitalNamePart.run() }
+let randomEmail = namePart.map { $0 + "@pointfree.co" }
+let randomId = int(in: 1...1_000)
+
+
+func zip2(_ a: Gen, _ b: Gen) -> Gen<(A, B)> {
+ return Gen<(A, B)> {
+ (a.run(), b.run())
+ }
+}
+
+zip2(randomId, randomName).run()
+zip2(randomId, randomName).run()
+zip2(randomId, randomName).run()
+
+func zip2(with f: @escaping (A, B) -> C) -> (Gen, Gen) -> Gen {
+ return { zip2($0, $1).map(f)}
+}
+
+func zip3(_ a: Gen, _ b: Gen, _ c: Gen) -> Gen<(A, B, C)> {
+ return zip2(a, zip2(b, c)).map { ($0, $1.0, $1.1) }
+}
+
+func zip3(with f: @escaping (A, B, C) -> D) -> (Gen, Gen, Gen) -> Gen {
+ return { zip3($0, $1, $2).map(f) }
+}
+
+let randomUser = zip3(with: User.init)(
+ randomUuid.map(User.Id.init),
+ randomName,
+ randomEmail
+)
+
+//let randomUser = Gen {
+// User(
+// id: randomId.run(),
+// name: randomName.run(),
+// email: randomEmail.run()
+// )
+//}
+
+print(randomUser.run())
+print(randomUser.run())
+print(randomUser.run())
+print(randomUser.run())
diff --git a/0032-arbitrary-pt2/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift b/0032-arbitrary-pt2/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift
new file mode 100644
index 00000000..906dd867
--- /dev/null
+++ b/0032-arbitrary-pt2/Arbitrary.playground/Pages/02-exercises.xcplaygroundpage/Contents.swift
@@ -0,0 +1,30 @@
+/*:
+ # Decodable Randomness: Part 2
+
+ ## Exercises
+
+ 1.) Redefine `Gen`'s base unit of randomness, `random`, which is a `Gen` to work with Swift 4.2's base unit of randomness, the `RandomNumberGenerator` protocol. The base random type should should change to `UInt64`.
+ */
+// TODO
+/*:
+ 2.) Swift 4.2's protocol-oriented solution allows us to define custom types that conform to `RandomNumberGenerator`. Update `Gen` to evaluate given any `RandomNumberGenerator` by changing `run`'s signature.
+ */
+// TODO
+/*:
+ 3.) Use a custom random number generator that can be configured with a stable seed to allow for the `Gen` type to predictably generate the same random value for a given seed.
+
+ You can look to [Nate Cook's playground](https://forums.swift.org/t/se-0202-random-unification/11313/30), shared on the Swift forums, or (for bonus points), you can define your own [linear congruential generator](https://en.wikipedia.org/wiki/Linear_congruential_generator) (or LCG).
+ */
+// TODO
+/*:
+ 4.) Write a helper that runs a property test for `XCTest`! A property test, given a generator and a block of code, will evaluate the block of code with a configurable number of random runs. If the block returns `true`, the property test passes. It it returns `false`, it fails. The signature should be the following.
+
+ func forAll(_ a: Gen, propertyShouldHold: (A) -> Bool)
+
+ It should, internally, call an `XCTAssert` function. Upon failure, print out the seed so that it can be reproduced.
+ */
+// TODO
+/*:
+ 5.) Enhance the `forAll` API to take the parameters `file: StaticString = #file` and `line: UInt = #line`, which can be passed to XCTest assertions in order to highlight the correct line on failure.
+ */
+// TODO
diff --git a/0032-arbitrary-pt2/Arbitrary.playground/Sources/Utils.swift b/0032-arbitrary-pt2/Arbitrary.playground/Sources/Utils.swift
new file mode 100644
index 00000000..9485da49
--- /dev/null
+++ b/0032-arbitrary-pt2/Arbitrary.playground/Sources/Utils.swift
@@ -0,0 +1,7 @@
+precedencegroup ForwardCompose {
+ associativity: left
+}
+infix operator >>>: ForwardCompose
+public func >>> (f: @escaping (A) -> B, g: @escaping (B) -> C) -> (A) -> C {
+ return { g(f($0)) }
+}
diff --git a/0032-arbitrary-pt2/Arbitrary.playground/contents.xcplayground b/0032-arbitrary-pt2/Arbitrary.playground/contents.xcplayground
new file mode 100644
index 00000000..f6677ad0
--- /dev/null
+++ b/0032-arbitrary-pt2/Arbitrary.playground/contents.xcplayground
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/0032-arbitrary-pt2/Arbitrary.xcworkspace/contents.xcworkspacedata b/0032-arbitrary-pt2/Arbitrary.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..b85f969b
--- /dev/null
+++ b/0032-arbitrary-pt2/Arbitrary.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/0032-arbitrary-pt2/Arbitrary.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/0032-arbitrary-pt2/Arbitrary.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/0032-arbitrary-pt2/Arbitrary.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/0032-arbitrary-pt2/README.md b/0032-arbitrary-pt2/README.md
new file mode 100644
index 00000000..57a9433b
--- /dev/null
+++ b/0032-arbitrary-pt2/README.md
@@ -0,0 +1,5 @@
+## [Point-Free](https://www.pointfree.co)
+
+> #### This directory contains code from Point-Free Episode: [Decodable Randomness: Part 2](https://www.pointfree.co/episodes/ep32-decodable-randomness-part-2)
+>
+> This week we compare our `Decodable` solution to building random structures with a composable solution involving the `Gen` type, exploring the differences and trade-offs of each approach. Along the way we'll rediscover a familiar old friend with a brand new application.