• Stars
    star
    266
  • Rank 154,103 (Top 4 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 6 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🎱 Composable, transformable, controllable randomness.

🎱 Gen

CI

Composable, transformable, controllable randomness.

Table of Contents

Motivation

Swift's randomness API is powerful and simple to use. It allows us to create random values from many basic types, such as booleans and numeric types, and it allows us to randomly shuffle arrays and pluck random elements from collections.

However, it does not make it easy for us to extend the randomness API, nor does it provide an API that is composable, which would allow us to create complex types of randomness from simpler pieces.

Gen is a lightweight wrapper over Swift's randomness APIs that makes it easy to build custom generators of any kind of value.

Examples

Gen's namesake type, Gen, is responsible for producing random values. Most often you will reach for one of the static variables inside Gen to get access to a Gen value:

Gen.bool
// Gen<Bool>

Rather than immediately producing a random value, Gen describes a random value that can be produced by calling its run method:

let myGen = Gen.bool
// Gen<Bool>

myGen.run() // true
myGen.run() // true
myGen.run() // false

Every random function that comes with Swift is also available as a static function on Gen:

Swift's API Gen's API
Int.random(in: 0...9) Gen.int(in: 0...9)
Double.random(in: 0...9) Gen.double(in: 0...9)
Bool.random() Gen.bool
[1, 2, 3].randomElement() Gen.element(of: [1, 2, 3])
[1, 2, 3].shuffled() Gen.shuffle([1, 2, 3])

The reason it is powerful to wrap randomness in the Gen type is that we can make the Gen type composable. For example, a generator of integers can be turned into a generator of numeric strings with a simple application of the map function:

let digit = Gen.int(in: 0...9)           // Gen<Int>
let stringDigit = digit.map(String.init) // Gen<String>

stringDigit.run() // "7"
stringDigit.run() // "1"
stringDigit.run() // "3"

Already this is a form of randomness that Swift's API's do not provide out of the box.

Gen provides many operators for generating new types of randomness, such as map, flatMap and zip, as well as helper functions for generating random arrays, sets, dictionaries, strings, distributions and more! A random password generator, for example, is just a few operators away.

// Take a generator of random letters and numbers.
let password = Gen.letterOrNumber
  // Generate 6-character strings of them.
  .string(of: .always(6))
  // Generate 3 segments of these strings.
  .array(of: .always(3))
  // And join them.
  .map { $0.joined(separator: "-") }

password.run() // "9BiGYA-fmvsOf-VYDtDv"
password.run() // "dS2MGr-FQSuC4-ZLEicl"
password.run() // "YusZGF-HILrCo-rNGfCA"

This kind of composition makes it simple to generate random values of anything.

// Use `zip` to combine generators together and build structures.

let randomPoint = zip(.int(in: -10...10), .int(in: -10...10))
  .map(CGPoint.init(x:y:))
// Gen<CGPoint>

But composability isn't the only reason the Gen type shines. By delaying the creation of random values until the run method is invoked, we allow ourselves to control randomness in circumstances where we need determinism, such as tests. The run method has an overload that takes a RandomNumberGenerator value, which is Swift's protocol that powers their randomness API. By default it uses the SystemRandomNumberGenerator, which is a good source of randomness, but we can also provide a seedable "pseudo" random number generator, so that we can get predictable results in tests:

var xoshiro = Xoshiro(seed: 0)
Gen.int(in: 0...9).run(using: &xoshiro) // "1"
Gen.int(in: 0...9).run(using: &xoshiro) // "0"
Gen.int(in: 0...9).run(using: &xoshiro) // "4"

xoshiro = Xoshiro(seed: 0)
Gen.int(in: 0...9).run(using: &xoshiro) // "1"
Gen.int(in: 0...9).run(using: &xoshiro) // "0"
Gen.int(in: 0...9).run(using: &xoshiro) // "4"

This means you don't have to sacrifice testability when leveraging randomness in your application.

For more examples of using Gen to build complex randomness, see our blog post on creating a Zalgo generator and our two-part video series (part 1 and part 2) on creating generative art.

Installation

If you want to use Gen in a project that uses SwiftPM, it's as simple as adding a dependencies clause to your Package.swift:

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-gen.git", from: "0.4.0")
]

Interested in learning more?

These concepts (and more) are explored thoroughly in Point-Free, a video series exploring functional programming and Swift hosted by Brandon Williams and Stephen Celis.

The design of this library was explored in the following Point-Free episodes:

video poster image

License

All modules are released under the MIT license. See LICENSE for details.

More Repositories

1

swift-composable-architecture

A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
Swift
12,150
star
2

swift-snapshot-testing

πŸ“Έ Delightful Swift snapshot testing.
Swift
3,737
star
3

isowords

Open source game built in SwiftUI and the Composable Architecture.
Swift
2,667
star
4

swift-navigation

Bringing simple and powerful navigation tools to all Swift platforms, inspired by SwiftUI.
Swift
1,950
star
5

swift-dependencies

A dependency management library inspired by SwiftUI's "environment."
Swift
1,527
star
6

swift-tagged

🏷 A wrapper type for safer, expressive code.
Swift
1,364
star
7

swift-overture

🎼 A library for function composition.
Swift
1,139
star
8

pointfreeco

🎬 The source for www.pointfree.co, a video series on functional programming and the Swift programming language.
Swift
1,098
star
9

episode-code-samples

πŸ’Ύ Point-Free episode code.
Swift
950
star
10

swift-case-paths

🧰 Case paths extends the key path hierarchy to enum cases.
Swift
905
star
11

swift-parsing

A library for turning nebulous data into well-structured data, with a focus on composition, performance, generality, and ergonomics.
Swift
847
star
12

swift-nonempty

🎁 A compile-time guarantee that a collection contains a value.
Swift
839
star
13

swift-custom-dump

A collection of tools for debugging, diffing, and testing your application's data structures.
Swift
795
star
14

swift-html

πŸ—Ί A Swift DSL for type-safe, extensible, and transformable HTML documents.
Swift
760
star
15

combine-schedulers

⏰ A few schedulers that make working with Combine more testable and more versatile.
Swift
701
star
16

swift-perception

Observable tools, backported.
Swift
536
star
17

swift-identified-collections

A library of data structures for working with collections of identifiable elements in an ergonomic, performant way.
Swift
529
star
18

swift-web

πŸ•Έ A collection of Swift server-side frameworks for handling HTML, CSS, routing and middleware.
Swift
481
star
19

swift-prelude

🎢 A collection of types and functions that enhance the Swift language.
Swift
469
star
20

swift-validated

πŸ›‚ A result type that accumulates multiple errors.
Swift
392
star
21

swift-issue-reporting

Report issues in your application and library code as Xcode runtime warnings, breakpoints, assertions, and do so in a testable manner.
Swift
361
star
22

swift-url-routing

A bidirectional router with more type safety and less fuss.
Swift
347
star
23

swift-concurrency-extras

Useful, testable Swift concurrency.
Swift
315
star
24

swift-macro-testing

Magical testing tools for Swift macros.
Swift
263
star
25

swift-clocks

⏰ A few clocks that make working with Swift concurrency more testable and more versatile.
Swift
258
star
26

syncups

A rebuild of Apple’s β€œScrumdinger” application using modern, best practices for SwiftUI development.
Swift
205
star
27

swift-enum-properties

🀝 Struct and enum data access in harmony.
Swift
200
star
28

composable-core-location

A library that bridges the Composable Architecture and Core Location.
Swift
108
star
29

vapor-routing

A bidirectional Vapor router with more type safety and less fuss.
Swift
89
star
30

swift-html-vapor

πŸ’§ Vapor plugin for type-safe, transformable HTML views.
Swift
84
star
31

swift-playground-templates

🏫 A collection of helpful Xcode playground templates.
Makefile
81
star
32

pointfreeco-server

Point-Free server code.
40
star
33

TrySyncUps

The starting project for our try! Swift 2024 Composable Architecture workshop.
Swift
38
star
34

composable-core-motion

A library that bridges the Composable Architecture and Core Motion.
Swift
29
star
35

swift-boundaries

🐣 Functional core, imperative shell.
Swift
28
star
36

swift-quickcheck

🏁 An implementation of QuickCheck in Swift.
Swift
25
star
37

swift-either

For those times you want A or B!
Swift
21
star
38

swift-algebras

Algebraic laws bundled into concrete data types.
20
star
39

swift-parser-printer

↔️ Parsing and printing
Swift
15
star
40

swift-html-kitura

☁️ Kitura plugin for type-safe, transformable HTML views.
Swift
14
star
41

swiftui-navigation

This package is now Swift Navigation:
Swift
13
star
42

homebrew-swift

Ruby
3
star
43

swift-bugs

3
star
44

Ccmark

Swift
2
star
45

.github

1
star