All Projects → pointfreeco → Swift Nonempty

pointfreeco / Swift Nonempty

Licence: mit
🎁 A compile-time guarantee that a collection contains a value.

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to Swift Nonempty

Identity
🆔 Type-safe identifiers in Swift
Stars: ✭ 282 (-51.79%)
Mutual labels:  type-safety
Graphaello
A Tool for Writing Declarative, Type-Safe and Data-Driven Applications in SwiftUI using GraphQL
Stars: ✭ 355 (-39.32%)
Mutual labels:  type-safety
Deep Learning Resources
由淺入深的深度學習資源 Collection of deep learning materials for everyone
Stars: ✭ 422 (-27.86%)
Mutual labels:  collections
Mammoth
A type-safe Postgres query builder for TypeScript.
Stars: ✭ 305 (-47.86%)
Mutual labels:  type-safety
Structopt
Parse command line arguments by defining a struct
Stars: ✭ 323 (-44.79%)
Mutual labels:  type-safety
Stream Parser
⚡ PHP7 / Laravel Multi-format Streaming Parser
Stars: ✭ 391 (-33.16%)
Mutual labels:  collections
Dry Struct
Typed struct and value objects
Stars: ✭ 263 (-55.04%)
Mutual labels:  type-safety
Knapsack
Collection pipeline library for PHP
Stars: ✭ 521 (-10.94%)
Mutual labels:  collections
For Data Science Beginners
Set of 📝 with 🔗 to help those who are Data Science beginners 🤖
Stars: ✭ 355 (-39.32%)
Mutual labels:  collections
Topshell
TopShell - a purely functional, reactive scripting language
Stars: ✭ 422 (-27.86%)
Mutual labels:  type-safety
Vuex Smart Module
Type safe Vuex module with powerful module features
Stars: ✭ 306 (-47.69%)
Mutual labels:  type-safety
Mlib
Library of generic and type safe containers in pure C language (C99 or C11) for a wide collection of container (comparable to the C++ STL).
Stars: ✭ 321 (-45.13%)
Mutual labels:  collections
Immutable
Immutable collections for Go
Stars: ✭ 392 (-32.99%)
Mutual labels:  collections
Scalacss
Super type-safe CSS for Scala and Scala.JS.
Stars: ✭ 293 (-49.91%)
Mutual labels:  type-safety
Kotlinx.collections.immutable
Immutable persistent collections for Kotlin
Stars: ✭ 465 (-20.51%)
Mutual labels:  collections
Pyhubweekly
每周精选Github上优质Python项目和小工具。
Stars: ✭ 264 (-54.87%)
Mutual labels:  collections
Awesomearticles
🗃 收集看到的内容特别棒的技术文章并会配有一段个人短评
Stars: ✭ 383 (-34.53%)
Mutual labels:  collections
Cdsa
A library of generic intrusive data structures and algorithms in ANSI C
Stars: ✭ 549 (-6.15%)
Mutual labels:  collections
Awesome Flake8 Extensions
A curated awesome list of flake8 extensions. Feel free to contribute! 🎓
Stars: ✭ 510 (-12.82%)
Mutual labels:  collections
Laravel Helpers
An extensive set of Laravel framework helper functions and collection macros.
Stars: ✭ 407 (-30.43%)
Mutual labels:  collections

🎁 NonEmpty

Swift 5.1 CI @pointfreeco

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

Carthage

If you use Carthage, you can add the following dependency to your Cartfile:

github "pointfreeco/swift-nonempty" ~> 0.2.2

CocoaPods

If your project uses CocoaPods, just add the following to your Podfile:

pod 'NonEmpty', '~> 0.2.2'

SwiftPM

If you want to use NonEmpty 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-nonempty.git", from: "0.2.2")
]

Xcode Sub-project

Submodule, clone, or download NonEmpty, and drag NonEmpty.xcodeproj into your project.

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].