{"id":20642699,"url":"https://github.com/typelift/focus","last_synced_at":"2025-08-21T00:31:07.646Z","repository":{"id":34520635,"uuid":"38462825","full_name":"typelift/Focus","owner":"typelift","description":"Optics for Swift","archived":false,"fork":false,"pushed_at":"2017-09-23T08:35:44.000Z","size":163,"stargazers_count":203,"open_issues_count":6,"forks_count":16,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-12-12T01:42:10.294Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"ddnet/ddnet","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/typelift.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-03T00:08:30.000Z","updated_at":"2024-11-02T18:01:57.000Z","dependencies_parsed_at":"2022-09-14T14:11:04.987Z","dependency_job_id":null,"html_url":"https://github.com/typelift/Focus","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typelift%2FFocus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typelift%2FFocus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typelift%2FFocus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/typelift%2FFocus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/typelift","download_url":"https://codeload.github.com/typelift/Focus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230471175,"owners_count":18231193,"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":[],"created_at":"2024-11-16T16:09:56.075Z","updated_at":"2024-12-19T17:09:09.905Z","avatar_url":"https://github.com/typelift.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Build Status](https://travis-ci.org/typelift/Focus.svg?branch=master)](https://travis-ci.org/typelift/Focus)\n[![Gitter chat](https://badges.gitter.im/DPVN/chat.png)](https://gitter.im/typelift/general?utm_source=share-link\u0026utm_medium=link\u0026utm_campaign=share-link)\n\n\nFocus\n=====\n\nFocus is an Optics library for Swift (where Optics includes `Lens`,\n`Prism`s, and `Iso`s) that is inspired by Haskell's\n[Lens](https://github.com/ekmett/lens) library.\n\nIntroduction\n============\n\nFocus exports a number of primitives that make it easy to establish\n*relations* between types.  Practically, a relation can be thought of\nas a particular way of viewing and modifying a structure.  The most\nfamous of these is a `Lens` or Functional Reference.  While there are\nan abundance of representations of a Lens (see\n[[van Laarhoven 09](http://www.twanvl.nl/blog/haskell/cps-functional-references)],\n[[Kmett et al. 12](http://lens.github.io)],\n[[Eidhof et al. 09](https://hackage.haskell.org/package/fclabels)], we\nhave chosen a\n[data-lens](https://hackage.haskell.org/package/data-lens)-like\nimplementation using the Indexed Store Comonad.  If all of that makes\nno sense, don't worry!  We have hidden all of this behind a simple\ninterface.\n\nProgramming With Lenses\n=======================\n\nThe easiest way to explain a lens is with a pair of functions\n\n```swift\nfunc get(structure : S) -\u003e A\nfunc set(pair : (self : S, newValue : A)) -\u003e S\n```\n\nThis should look quite familiar to you!  After all, Swift includes\nsyntax for this very pattern\n\n```swift\nfinal class Foo {\n\tvar bar : Qux {\n\t\tget { //.. }\n\t\tset(newValue) { //.. }\n\t}\n}\n```\n\nSo what a lens actually lets you do is decouple the ability to *focus*\non particular bits and pieces of your data types. Moreover, lenses,\nlike properties, compose freely with other compatible lenses but with\nnormal function composition (denoted `•`) instead of the usual\ndot-notation.  What sets Lenses apart from straight properties is\nevery part of the process is immutable.  A lens performs replacement\nof the entire structure, a property performs replacement of a mutable\nvalue within that structure.\n\nAll of these properties, flexibility immutability, and composability,\ncome together to enable a powerful set of operations that allow the\nprogrammer to view a structure and its parts at any depth and any\nangle, not simply those provided by properties.\n\nPractical Lenses\n================\n\nFor example, say we have this set of structures for working with a\nflight tracking app:\n\n```swift\nimport Foundation\nimport Focus\n\nenum Status {\n\tcase Early\n\tcase OnTime\n\tcase Late\n}\n\nstruct Plane {\n\tlet model : String\n\tlet freeSeats : UInt\n\tlet takenSeats : UInt\n\tlet status : Status\n\tvar totalSeats : UInt {\n\t\treturn self.freeSeats + self.takenSeats\n\t}\n}\n\nstruct Gate {\n\tlet number : UInt\n\tlet letter : Character\n}\n\nstruct BoardingPass {\n\tlet plane : Plane\n\tlet gate : Gate\n\tlet departureDate : NSDate\n\tlet arrivalDate : NSDate\n}\n\n```\n\nStarting with a `BoardingPass`, getting our flight status is trivial\n\n```swift\nlet plane = Plane(model: \"SpaceX Raptor\", freeSeats: 4, takenSeats: 0, status: .OnTime)\nlet gate = Gate(number: 1, letter: \"A\")\nlet pass = BoardingPass(plane: plane\n\t\t\t\t\t, gate: gate\n\t\t\t\t\t, departureDate: NSDate.distantFuture()\n\t\t\t\t\t, arrivalDate: NSDate.distantFuture())\nlet status = pass.plane.status\n```\n\nHowever, in order to update the status on the boarding pass without\nlenses, we'd have to go through this rigamarole every time:\n\n```swift\nlet oldPass = BoardingPass(/* */)\n// Apparently, we're actually flying Allegiant\nlet newFlight = Plane(model: oldPass.plane.model\n\t\t\t\t\t, freeSeats: oldPass.plane.freeSeats\n\t\t\t\t\t, takenSeats: oldPass.plane.takenSeats\n\t\t\t\t\t, status: .Late)\nlet newPass = BoardingPass(plane: newFlight\n\t\t\t\t\t\t, gate: oldPass.gate\n\t\t\t\t\t\t, departureDate: oldPass.departureDate\n\t\t\t\t\t\t, arrivalDate: oldPass.arrivalDate)\n```\n\nAfter defining a few lenses, this is what we can do instead:\n\n```swift\n// The composite of two lenses is itself a lens\nlet newPass = (BoardingPass._plane • Plane._status).set(oldPass, .Late)\n```\n\nHere's the definition of those lenses:\n\n```swift\nextension BoardingPass {\n\tstatic var _plane : SimpleLens\u003cBoardingPass, Plane\u003e {\n\t\treturn SimpleLens(get: {\n\t\t\treturn $0.plane\n\t\t}, set: { (oldPass, newP) in\n\t\t\treturn BoardingPass(plane: newP\n\t\t\t\t\t\t\t, gate: oldPass.gate\n\t\t\t\t\t\t\t, departureDate: oldPass.departureDate\n\t\t\t\t\t\t\t, arrivalDate: oldPass.arrivalDate)\n\t\t})\n\t}\n}\n\nextension Plane {\n\tstatic var _status : SimpleLens\u003cPlane, Status\u003e {\n\t\treturn SimpleLens(get: {\n\t\t\treturn $0.status\n\t\t}, set: { (oldP, newS) in\n\t\t\treturn Plane( model: oldP.model\n\t\t\t\t\t\t, freeSeats: oldP.freeSeats\n\t\t\t\t\t\t, takenSeats: oldP.takenSeats\n\t\t\t\t\t\t, status: newS)\n\t\t})\n\t}\n}\n```\n\n\nWe've only scratched the surface of the power of Lenses, and we\nhaven't even touched the other members of the family of optics\nexported by Focus.  For more on the Lens Family, check out the additional\nsources below and the implementation files for each family member.\n\nFurther Reading\n===============\n\n- The [Lens Library](http://lens.github.io)'s website.\n- [Aditya Bhargava](http://adit.io/posts/2013-07-22-lenses-in-pictures.html)'s marvelous illustrated guide to lenses.\n- [Haskell for All](http://www.haskellforall.com/2013/05/program-imperatively-using-haskell.html)'s Gabriel Gonzalez explains how to program imperatively using lenses\n- [Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125).\n- Edward Kmett's talk on [Lenses, Folds, and Traversals](https://www.youtube.com/watch?v=cefnmjtAolY).\n- In fact, pretty much everything [Edward Kmett](https://www.youtube.com/user/edwardkmett/videos) has talked about.\n\nSystem Requirements\n===================\n\nFocus supports OS X 10.9+ and iOS 8.0+.\n\nLicense\n=======\n\nFocus is released under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftypelift%2Ffocus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftypelift%2Ffocus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftypelift%2Ffocus/lists"}