• Stars
    star
    839
  • Rank 54,338 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 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

🎁 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
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-custom-dump

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

swift-html

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

combine-schedulers

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

swift-perception

Observable tools, backported.
Swift
536
star
16

swift-identified-collections

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

swift-web

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

swift-prelude

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

swift-validated

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

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
21

swift-url-routing

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

swift-concurrency-extras

Useful, testable Swift concurrency.
Swift
315
star
23

swift-gen

🎱 Composable, transformable, controllable randomness.
Swift
266
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