{"id":13908351,"url":"https://github.com/pointfreeco/swift-nonempty","last_synced_at":"2025-04-08T12:09:26.634Z","repository":{"id":32686874,"uuid":"138501232","full_name":"pointfreeco/swift-nonempty","owner":"pointfreeco","description":"🎁 A compile-time guarantee that a collection contains a value.","archived":false,"fork":false,"pushed_at":"2024-07-04T01:05:05.000Z","size":97,"stargazers_count":860,"open_issues_count":6,"forks_count":40,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-04-01T11:02:01.654Z","etag":null,"topics":["collections","conditional-conformance","swift","type-safety"],"latest_commit_sha":null,"homepage":"https://www.pointfree.co/episodes/ep20-nonempty","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pointfreeco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-24T17:16:53.000Z","updated_at":"2025-04-01T07:48:40.000Z","dependencies_parsed_at":"2023-02-17T23:31:07.016Z","dependency_job_id":"6d1206ce-cf55-438c-9138-92f4f8e5bc8f","html_url":"https://github.com/pointfreeco/swift-nonempty","commit_stats":{"total_commits":46,"total_committers":16,"mean_commits":2.875,"dds":0.5652173913043479,"last_synced_commit":"1fd3f067ebc3900d200aa7e977fdb614cc5533bc"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pointfreeco%2Fswift-nonempty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pointfreeco%2Fswift-nonempty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pointfreeco%2Fswift-nonempty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pointfreeco%2Fswift-nonempty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pointfreeco","download_url":"https://codeload.github.com/pointfreeco/swift-nonempty/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247838444,"owners_count":21004580,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["collections","conditional-conformance","swift","type-safety"],"created_at":"2024-08-06T23:02:39.809Z","updated_at":"2025-04-08T12:09:26.614Z","avatar_url":"https://github.com/pointfreeco.png","language":"Swift","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"# 🎁 NonEmpty\n\n[![CI](https://github.com/pointfreeco/swift-nonempty/workflows/CI/badge.svg)](https://actions-badge.atrox.dev/pointfreeco/swift-nonempty/goto)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpointfreeco%2Fswift-nonempty%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/pointfreeco/swift-nonempty)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fpointfreeco%2Fswift-nonempty%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/pointfreeco/swift-nonempty)\n\nA compile-time guarantee that a collection contains a value.\n\n## Motivation\n\nWe 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:\n\n```swift\n// 1.) A non-empty array of integers\nlet xs = NonEmpty\u003c[Int]\u003e(1, 2, 3, 4)\nxs.first + 1 // `first` is non-optional since it's guaranteed to be present\n\n// 2.) A non-empty set of integers\nlet ys = NonEmpty\u003cSet\u003cInt\u003e\u003e(1, 1, 2, 2, 3, 4)\nys.forEach { print($0) } // =\u003e 1, 2, 3, 4\n\n// 3.) A non-empty dictionary of values\nlet zs = NonEmpty\u003c[Int: String]\u003e((1, \"one\"), [2: \"two\", 3: \"three\"])\n\n// 4.) A non-empty string\nlet helloWorld = NonEmpty\u003cString\u003e(\"H\", \"ello World\")\nprint(\"\\(helloWorld)!\") // \"Hello World!\"\n```\n\n## Applications\n\nThere 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:\n\n### Strengthen 1st party APIs\n\nMany APIs take and return empty-able arrays when they can in fact guarantee that the arrays are non-empty. Consider a `groupBy` function:\n\n```swift\nextension Sequence {\n  func groupBy\u003cA\u003e(_ f: (Element) -\u003e A) -\u003e [A: [Element]] {\n    // Unimplemented\n  }\n}\n\nArray(1...10)\n  .groupBy { $0 % 3 }\n// [0: [3, 6, 9], 1: [1, 4, 7, 10], 2: [2, 5, 8]]\n```\n\nHowever, 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:\n\n```swift\nextension Sequence {\n  func groupBy\u003cA\u003e(_ f: (Element) -\u003e A) -\u003e [A: NonEmpty\u003c[Element]\u003e] {\n    // Unimplemented\n  }\n}\n```\n\n### Better interface with 3rd party APIs\n\nSometimes 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](https://graphql.org/). Here is a very simple query builder and printer:\n\n```swift\nenum UserField: String { case id, name, email }\n\nfunc query(_ fields: Set\u003cUserField\u003e) -\u003e String {\n  return ([\"{\"] + fields.map { \"  \\($0.rawValue)\" } + [\"}\"])\n    .joined()\n}\n\nprint(query([.name, .email]))\n// {\n//   name\n//   email\n// }\n\nprint(query([]))\n// {\n// }\n```\n\nThis 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:\n\n```swift\nfunc query(_ fields: NonEmptySet\u003cUserField\u003e) -\u003e String {\n  return ([\"{\"] + fields.map { \"  \\($0.rawValue)\" } + [\"}\"])\n    .joined()\n}\n\nprint(query(.init(.name, .email)))\n// {\n//   name\n//   email\n// }\n\nprint(query(.init()))\n// 🛑 Does not compile\n```\n\n### More expressive data structures\n\nA 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:\n\n```swift\nenum Validated\u003cValue, Error\u003e {\n  case valid(Value)\n  case invalid([Error])\n}\n```\n\nA 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:\n\n```swift\nlet validatedPassword: Validated\u003cString, String\u003e =\n  .invalid([\"Password is too short.\", \"Password must contain at least one number.\"])\n```\n\nThis 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:\n\n```swift\nlet validatedPassword: Validated\u003cString, String\u003e = .invalid([]) // ???\n```\n\nInstead, we should strengthen the `Validated` type to use a non-empty array:\n\n```swift\nenum Validated\u003cValue, Error\u003e {\n  case valid(Value)\n  case invalid(NonEmptyArray\u003cError\u003e)\n}\n```\n\nAnd now this is a compiler error:\n\n```swift\nlet validatedPassword: Validated\u003cString, String\u003e = .invalid(.init([])) // 🛑\n```\n\n## Installation\n\nIf you want to use NonEmpty in a project that uses [SwiftPM](https://swift.org/package-manager/), it's as simple as adding a dependency to your `Package.swift`:\n\n``` swift\ndependencies: [\n  .package(url: \"https://github.com/pointfreeco/swift-nonempty.git\", from: \"0.3.0\")\n]\n```\n\n## Interested in learning more?\n\nThese concepts (and more) are explored thoroughly in [Point-Free](https://www.pointfree.co), a video series exploring functional programming and Swift hosted by [Brandon Williams](https://twitter.com/mbrandonw) and [Stephen Celis](https://twitter.com/stephencelis).\n\nNonEmpty was first explored in [Episode #20](https://www.pointfree.co/episodes/ep20-nonempty):\n\n\u003ca href=\"https://www.pointfree.co/episodes/ep20-nonempty\"\u003e\n  \u003cimg alt=\"video poster image\" src=\"https://d3rccdn33rt8ze.cloudfront.net/episodes/0020.jpeg\" width=\"480\"\u003e\n\u003c/a\u003e\n\n## License\n\nAll modules are released under the MIT license. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpointfreeco%2Fswift-nonempty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpointfreeco%2Fswift-nonempty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpointfreeco%2Fswift-nonempty/lists"}