Skip to content

Commit

Permalink
Add 31
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Sep 24, 2018
1 parent af9deee commit 8f63c71
Show file tree
Hide file tree
Showing 11 changed files with 34 additions and 162 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
path = 0025-zip-pt3/Vendor/swift-nonempty
url = https://github.com/pointfreeco/swift-nonempty.git
[submodule "0031-arbitrary/Vendor/Tagged"]
path = 0031-arbitrary/Vendor/Tagged
path = 0031-arbitrary-pt1/Vendor/Tagged
url = https://github.com/pointfreeco/swift-tagged.git
Original file line number Diff line number Diff line change
Expand Up @@ -171,165 +171,13 @@ try Date(from: ArbitraryDecoder())
try Date(from: ArbitraryDecoder())
//try UUID(from: ArbitraryDecoder())

import Tagged

struct User: Decodable {
typealias Id = Tagged<User, UUID>

let id: Id
let id: Int
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<A> {
let run: () -> A
}

import Darwin
let random = Gen(run: arc4random)

extension Gen {
func map<B>(_ f: @escaping (A) -> B) -> Gen<B> {
return Gen<B> { f(self.run()) }
}
}

let uint64 = Gen<UInt64> {
let lower = UInt64(random.run())
let upper = UInt64(random.run()) << 32
return lower + upper
}

func int(in range: ClosedRange<Int>) -> Gen<Int> {
return Gen<Int> {
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<A>(of xs: [A]) -> Gen<A?> {
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<Int>) -> Gen<[A]> {
return Gen<[A]> {
Array(repeating: (), count: count.run())
.map { self.run() }
}
}
}

func uint8(in range: ClosedRange<UInt8>) -> Gen<UInt8> {
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<Int>) -> Gen<String> {
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<String> { capitalNamePart.run() + " " + capitalNamePart.run() }
let randomEmail = namePart.map { $0 + "@pointfree.co" }
let randomId = int(in: 1...1_000)


func zip2<A, B>(_ a: Gen<A>, _ b: Gen<B>) -> 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<A, B, C>(with f: @escaping (A, B) -> C) -> (Gen<A>, Gen<B>) -> Gen<C> {
return { zip2($0, $1).map(f)}
}

func zip3<A, B, C>(_ a: Gen<A>, _ b: Gen<B>, _ c: Gen<C>) -> Gen<(A, B, C)> {
return zip2(a, zip2(b, c)).map { ($0, $1.0, $1.1) }
}

func zip3<A, B, C, D>(with f: @escaping (A, B, C) -> D) -> (Gen<A>, Gen<B>, Gen<C>) -> Gen<D> {
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())
print(try User(from: ArbitraryDecoder()))
print(try User(from: ArbitraryDecoder()))
print(try User(from: ArbitraryDecoder()))
print(try User(from: ArbitraryDecoder()))
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*:
# Decodable Randomness

## Exercises

1.) We skipped over the `allKeys` property of the `KeyedDecodingContainerProtocol`, but it's what's necessary to decode dictionaries of values. On initialization of the `KeyedDecodingContainer`, generate a random number of random `CodingKey`s to populate this property.

You'll need to return `true` from `contains(_ key: Key)`.

Decode a few random dictionaries of various decodable keys and values. What are some of the limitations of decoding dictionaries?
*/
// TODO
/*:
2.) Create a new `UnkeyedContainer` struct that conforms to the `UnkeyedContainerProtocol` and return it from the `unkeyedContainer()` method of `ArbitraryDecoder`. As with the `KeyedDecodingContainer`, you can delete the same `decode` methods and have them delegate to the `SingleValueContainer`.

The `count` property can be used to generate a randomly-sized container, while `currentIndex` and `isAtEnd` can be used to let the decoder know how far along it is. Generate a random `count`, default the `currentIndex` to `0`, and define `isAtEnd` as a computed property using these values. The `currentIndex` property should increment whenever `superDecoder` is called.

Decode a few random arrays of various decodable elements.
*/
// TODO
7 changes: 7 additions & 0 deletions 0031-arbitrary-pt1/Arbitrary.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='macos' display-mode='raw' executeOnSourceChanges='false'>
<pages>
<page name='01-episode'/>
<page name='02-exercises'/>
</pages>
</playground>
File renamed without changes.
4 changes: 0 additions & 4 deletions 0031-arbitrary/Arbitrary.playground/contents.xcplayground

This file was deleted.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ This repository is the home of code written on episodes of
1. [An HTML DSL](0028-html-dsl)
1. [DSLs vs. Templating Languages](0029-dsls-vs-templating-languages)
1. [Composable Randomness](0030-composable-randomness)
1. [Decodable Randomness](0031-arbitrary-pt1)

0 comments on commit 8f63c71

Please sign in to comment.