• Stars
    star
    817
  • Rank 53,514 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 6 years ago
  • Updated about 2 months ago

Reviews

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

Repository Details

๐ŸŽ A compile-time guarantee that a collection contains a value.

๐ŸŽ NonEmpty

CI

A compile-time guarantee that a collection contains a value.

Motivation

We often work with collections that should never be empty, but the type system makes no such guarantees, so we're forced to handle that empty case, often with if and guard statements. NonEmpty is a lightweight type that can transform any collection type into a non-empty version. Some examples:

// 1.) A non-empty array of integers
let xs = NonEmpty<[Int]>(1, 2, 3, 4)
xs.first + 1 // `first` is non-optional since it's guaranteed to be present

// 2.) A non-empty set of integers
let ys = NonEmpty<Set<Int>>(1, 1, 2, 2, 3, 4)
ys.forEach { print($0) } // => 1, 2, 3, 4

// 3.) A non-empty dictionary of values
let zs = NonEmpty<[Int: String]>((1, "one"), [2: "two", 3: "three"])

// 4.) A non-empty string
let helloWorld = NonEmpty<String>("H", "ello World")
print("\(helloWorld)!") // "Hello World!"

Applications

There are many applications of non-empty collection types but it can be hard to see since the Swift standard library does not give us this type. Here are just a few such applications:

Strengthen 1st party APIs

Many APIs take and return empty-able arrays when they can in fact guarantee that the arrays are non-empty. Consider a groupBy function:

extension Sequence {
  func groupBy<A>(_ f: (Element) -> A) -> [A: [Element]] {
    // Unimplemented
  }
}

Array(1...10)
  .groupBy { $0 % 3 }
// [0: [3, 6, 9], 1: [1, 4, 7, 10], 2: [2, 5, 8]]

However, the array [Element] inside the return type [A: [Element]] can be guaranteed to never be empty, for the only way to produce an A is from an Element. Therefore the signature of this function could be strengthened to be:

extension Sequence {
  func groupBy<A>(_ f: (Element) -> A) -> [A: NonEmpty<[Element]>] {
    // Unimplemented
  }
}

Better interface with 3rd party APIs

Sometimes a 3rd party API we interact with requires non-empty collections of values, and so in our code we should use non-empty types so that we can be sure to never send an empty values to the API. A good example of this is GraphQL. Here is a very simple query builder and printer:

enum UserField: String { case id, name, email }

func query(_ fields: Set<UserField>) -> String {
  return (["{"] + fields.map { "  \($0.rawValue)" } + ["}"])
    .joined()
}

print(query([.name, .email]))
// {
//   name
//   email
// }

print(query([]))
// {
// }

This last query is a programmer error, and will cause the GraphQL server to send back an error because it is not valid to send an empty query. We can prevent this from ever happening by instead forcing our query builder to work with non-empty sets:

func query(_ fields: NonEmptySet<UserField>) -> String {
  return (["{"] + fields.map { "  \($0.rawValue)" } + ["}"])
    .joined()
}

print(query(.init(.name, .email)))
// {
//   name
//   email
// }

print(query(.init()))
// ๐Ÿ›‘ Does not compile

More expressive data structures

A popular type in the Swift community (and other languages), is the Result type. It allows you to express a value that can be successful or be a failure. There's a related type that is also handy, called the Validated type:

enum Validated<Value, Error> {
  case valid(Value)
  case invalid([Error])
}

A value of type Validated is either valid, and hence comes with a Value, or it is invalid, and comes with an array of errors that describe what all is wrong with the value. For example:

let validatedPassword: Validated<String, String> =
  .invalid(["Password is too short.", "Password must contain at least one number."])

This is useful because it allows you to describe all of the things wrong with a value, not just one thing. However, it doesn't make a lot of sense if we use an empty array of the list of validation errors:

let validatedPassword: Validated<String, String> = .invalid([]) // ???

Instead, we should strengthen the Validated type to use a non-empty array:

enum Validated<Value, Error> {
  case valid(Value)
  case invalid(NonEmptyArray<Error>)
}

And now this is a compiler error:

let validatedPassword: Validated<String, String> = .invalid(.init([])) // ๐Ÿ›‘

Installation

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

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-nonempty.git", from: "0.3.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.

NonEmpty was first explored in Episode #20:

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
10,990
star
2

swift-snapshot-testing

๐Ÿ“ธ Delightful Swift snapshot testing.
Swift
3,559
star
3

isowords

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

swiftui-navigation

Tools for making SwiftUI navigation simpler, more ergonomic and more precise.
Swift
1,654
star
5

swift-dependencies

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

swift-tagged

๐Ÿท A wrapper type for safer, expressive code.
Swift
1,289
star
7

swift-overture

๐ŸŽผ A library for function composition.
Swift
1,115
star
8

pointfreeco

๐ŸŽฌ The source for www.pointfree.co, a video series on functional programming and the Swift programming language.
Swift
1,054
star
9

episode-code-samples

๐Ÿ’พ Point-Free episode code.
Swift
922
star
10

swift-case-paths

๐Ÿงฐ Case paths extends the key path hierarchy to enum cases.
Swift
852
star
11

swift-parsing

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

swift-custom-dump

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

swift-html

๐Ÿ—บ A Swift DSL for type-safe, extensible, and transformable HTML documents.
Swift
723
star
14

combine-schedulers

โฐ A few schedulers that make working with Combine more testable and more versatile.
Swift
671
star
15

swift-web

๐Ÿ•ธ A collection of Swift server-side frameworks for handling HTML, CSS, routing and middleware.
Swift
477
star
16

swift-identified-collections

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

swift-prelude

๐ŸŽถ A collection of types and functions that enhance the Swift language.
Swift
456
star
18

swift-perception

Observable tools, backported.
Swift
399
star
19

swift-validated

๐Ÿ›‚ A result type that accumulates multiple errors.
Swift
389
star
20

swift-url-routing

A bidirectional router with more type safety and less fuss.
Swift
321
star
21

swift-concurrency-extras

Useful, testable Swift concurrency.
Swift
267
star
22

swift-gen

๐ŸŽฑ Composable, transformable, controllable randomness.
Swift
261
star
23

swift-clocks

โฐ A few clocks that make working with Swift concurrency more testable and more versatile.
Swift
229
star
24

swift-enum-properties

๐Ÿค Struct and enum data access in harmony.
Swift
198
star
25

xctest-dynamic-overlay

Define XCTest assertion helpers directly in your application and library code.
Swift
197
star
26

swift-macro-testing

Magical testing tools for Swift macros.
Swift
192
star
27

syncups

A rebuild of Appleโ€™s โ€œScrumdingerโ€ application using modern, best practices for SwiftUI development.
Swift
153
star
28

composable-core-location

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

vapor-routing

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

swift-html-vapor

๐Ÿ’ง Vapor plugin for type-safe, transformable HTML views.
Swift
82
star
31

swift-playground-templates

๐Ÿซ A collection of helpful Xcode playground templates.
Makefile
80
star
32

pointfreeco-server

Point-Free server code.
39
star
33

swift-boundaries

๐Ÿฃ Functional core, imperative shell.
Swift
27
star
34

composable-core-motion

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

swift-quickcheck

๐Ÿ An implementation of QuickCheck in Swift.
Swift
24
star
36

swift-algebras

Algebraic laws bundled into concrete data types.
19
star
37

swift-either

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

swift-parser-printer

โ†”๏ธ Parsing and printing
Swift
14
star
39

swift-html-kitura

โ˜๏ธ Kitura plugin for type-safe, transformable HTML views.
Swift
13
star
40

homebrew-swift

Ruby
2
star
41

swift-bugs

2
star
42

Ccmark

Swift
1
star