{"id":15293529,"url":"https://github.com/mattpolzin/poly","last_synced_at":"2025-09-17T20:27:41.813Z","repository":{"id":36029127,"uuid":"165458166","full_name":"mattpolzin/Poly","owner":"mattpolzin","description":"A light-weight general-purpose library to help represent situations where the type of a value is one of a set of types. An alternative to type erasure.","archived":false,"fork":false,"pushed_at":"2024-03-27T22:14:37.000Z","size":73,"stargazers_count":20,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-10T23:59:26.592Z","etag":null,"topics":["library","swift","type-erasure"],"latest_commit_sha":null,"homepage":"","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/mattpolzin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"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":"2019-01-13T02:37:52.000Z","updated_at":"2025-06-17T14:22:10.000Z","dependencies_parsed_at":"2024-10-14T22:21:17.115Z","dependency_job_id":"6192290e-b13c-4cc2-b593-439de1264f32","html_url":"https://github.com/mattpolzin/Poly","commit_stats":{"total_commits":48,"total_committers":2,"mean_commits":24.0,"dds":0.02083333333333337,"last_synced_commit":"836e5c5d0b4f1d799931637a62e430f789e95b24"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/mattpolzin/Poly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattpolzin%2FPoly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattpolzin%2FPoly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattpolzin%2FPoly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattpolzin%2FPoly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattpolzin","download_url":"https://codeload.github.com/mattpolzin/Poly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattpolzin%2FPoly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275658300,"owners_count":25504776,"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","status":"online","status_checked_at":"2025-09-17T02:00:09.119Z","response_time":84,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["library","swift","type-erasure"],"created_at":"2024-09-30T16:49:52.971Z","updated_at":"2025-09-17T20:27:41.803Z","avatar_url":"https://github.com/mattpolzin.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Poly\n[![MIT license](http://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) [![Swift 6.0+](http://img.shields.io/badge/Swift-6.x-blue.svg)](https://swift.org)\n\nPoly is a small library to provide an alternative to rolling your own type-erasure when a value has one of a small set of Types. The Poly library contains the Types `Poly1`, `Poly2`, `Poly3`, etc. for representing increasingly larger pools of possible Types. `Poly2` is isomorphic to `Either` (a common generic functional programming Type).\n\n## Dev Environment\n### Prerequisites\n1. Swift 6.0+\n2. Swift Package Manager\n\n## Usage\n\nUsage will be explained by way of an example. Suppose you have some code with three different Types: `Dog`, `Cat`, and `Rat`. You also have a protocol, `Animal`, that they all belong to.\n\nIf you need to store animals of all three Types in one place (maybe an array), that looks like:\n```swift\nlet dog = Dog()\nlet cat = Cat()\nlet rat = Rat()\n\nlet animals: [Poly3\u003cDog, Cat, Rat\u003e] = [\n  .init(dog),\n  .init(cat),\n  .init(rat)\n]\n```\n\nTo access all animals of a certain type, you can use subscripting like:\n```swift\nlet dogs = animals[Dog.self]\nlet cats = animals[Cat.self]\nlet rats = animals[Rat.self]\n```\n\nYou can get the `Dog`, `Cat`, or `Rat` value back out again, but you won't get any guarantees of which Type is being stored in a given `Poly`:\n```swift\nlet animal = Poly3\u003cDog, Cat, Rat\u003e(Dog())\n\nlet maybeDog: Dog? = animal.a\nlet maybeCat: Cat? = animal.b\nlet maybeRat: Rat? = animal.c\n```\nOr use the subscript operator to make accessing one of the possible values of a `Poly` a bit more intuitive:\n```swift\nlet maybeDog2 = animal[Dog.self]\nlet maybeCat2 = animal[Cat.self]\nlet maybeRat2 = animal[Rat.self]\n```\nOr switch over the possible values:\n```swift\nswitch animal {\n  case .a(let dog):\n    print(dog)\n  case .b(let cat):\n    print(cat)\n  case .c(let rat):\n    print(rat)\n}\n```\nOr access a type-erased value:\n```swift\nlet someAnimal: Any = animal.value \n```\n\nYou might consider making a typealias to make your life easier:\n```swift\ntypealias AnyAnimal = Poly3\u003cDog, Cat, Rat\u003e\n```\n\nYou also might find it worthwhile to go the extra mile and add `Animal` conformance to `Poly\u003cDog, Cat, Rat\u003e`:\n```swift\nprotocol Animal {\n  var speak: String { get }\n}\n\nextension Poly3: Animal where A == Dog, B == Cat, C == Rat {\n  var speak: String {\n    switch self {\n      case .a(let animal as Animal),\n           .b(let animal as Animal),\n           .c(let animal as Animal):\n          return animal.speak\n    }\n  }\n}\n```\nSo now you can take the array of animals from the first example above and:\n```swift\nlet animalSounds = animals.map { $0.speak }\n```\n\n### `Codable`\n\nAll of the `Poly`types are `Encodable`/`Decodable` when every generic on which they are specialized is `Ecodable`/`Decodable`. This works by attempting to encode or decode each type and using the first successfuly attempt. That means the behavior only works as expected if all of the types have different encoding/decoding requirements or at least types are ordered with more restrictive rules coming first.\n\nFor example, given the following type\n```swift\nstruct Type1: Decodable {\n    let x: Poly2\u003cDouble, Int\u003e \n}\n```\n\nan integer value will never be decoded as an `Int` because `Double` is capable of decoding all `Int` values.\n\nYou can fix this by swapping the order to `Poly2\u003cInt, Double\u003e`. Now `Poly` will attempt to decode an `Int` and only attempt to decode a `Double` if an `Int` was not found.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattpolzin%2Fpoly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattpolzin%2Fpoly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattpolzin%2Fpoly/lists"}