{"id":15629532,"url":"https://github.com/sharplet/state","last_synced_at":"2025-04-29T09:57:56.228Z","repository":{"id":25518044,"uuid":"28949832","full_name":"sharplet/State","owner":"sharplet","description":"An implementation of the State monad in Swift.","archived":false,"fork":false,"pushed_at":"2015-01-18T22:30:07.000Z","size":324,"stargazers_count":10,"open_issues_count":4,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-29T09:57:50.329Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sharplet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-08T05:05:24.000Z","updated_at":"2022-06-27T05:31:11.000Z","dependencies_parsed_at":"2022-08-24T05:10:49.807Z","dependency_job_id":null,"html_url":"https://github.com/sharplet/State","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sharplet%2FState","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sharplet%2FState/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sharplet%2FState/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sharplet%2FState/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sharplet","download_url":"https://codeload.github.com/sharplet/State/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251480070,"owners_count":21596016,"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-10-03T10:27:30.793Z","updated_at":"2025-04-29T09:57:56.209Z","avatar_url":"https://github.com/sharplet.png","language":"Swift","readme":"# Haskell's State monad in Swift\n\nState is a µframework providing a Swift port of Haskell's State monad.\nIt is an exploration of how Haskell-style monadic APIs might work in Swift.\n\nCurrently, State is more of a curiosity intended for educational purposes.\nI don't recommend it's use in production code.\n\n\n## Background\n\nHaskell's data types are immutable, and its functions *pure*.\nFunctions can't modify their arguments, nor mutate any global state.\nThis kind of code is impossible:\n\n```objc\nNSMutableArray *stack = @[].mutableCopy;\n\n[stack addObject:@1];     // =\u003e @[@1]\n[stack addObject:@2];     // =\u003e @[@1, @2]\n[stack lastObject];       // =\u003e @2\n[stack removeLastObject]; // =\u003e @[@1]\n```\n\nThis example uses an `NSMutableArray` as a kind of stack, using `addObject:` to push new elements, `lastObject` to see the top element, and `removeLastObject` to pop the top element from the stack.\n\nRemember that functions in Haskell can't modify their arguments, but can only return a value.\nIf you have a `pop` function that returns the top of the stack, it must also return a new stack that has the top element removed.\n\n```haskell\ntype Stack = [Int]\n\n-- take a stack, and return a tuple containing the top of the stack and a new\n-- stack with the element removed\npop :: Stack -\u003e (Int, Stack)\n```\n\nHowever, sequencing multiple stateful functions like this becomes rather tedious:\n\n```haskell\nlet stack = [1,2,3]\nlet (top, stack2) = pop stack\nlet (top2, stack3) = pop stack2\n```\n\nHaskell provides the `State` monad, which allows much more natural sequencing of stateful computations:\n\n```haskell\nimport Control.Monad.State\n\ntype Stack = [Int]\n\n-- a function that accepts an int and a stack and returns a new stack with the\n-- int on top\npush :: Int -\u003e State Stack ()\npush x = state $ \\xs -\u003e ((), x:xs)\n\n-- a function that accepts a stack and returns the top and a new stack with\n-- the item removed\npop :: State Stack Int\npop = state $ \\(x:xs) -\u003e (x,xs)\n\n-- stateful computation which pops two elements from a stack and collects them into a list\npopTwice :: State Stack [Int]\npopTwice = do\n    a \u003c- pop\n    b \u003c- pop\n    return [a,b]\n```\n\n(These examples are adapted from Learn You A Haskell's [section on the State monad](http://learnyouahaskell.com/for-a-few-monads-more#state).)\n\n\n## The State monad in Swift\n\nHow might this look in Swift?\n\n```swift\ntypealias Stack = [Int]\n\nfunc push(x: Int) -\u003e State\u003cStack, ()\u003e {\n    return State { xs in ((), [x] + xs) }\n}\n\nfunc pop() -\u003e State\u003cStack, Int\u003e {\n    return State { xs in (xs.first!, Array(dropFirst(xs))) }\n}\n\n// We don't have the convenience of `do`-notation here, but we can achieve the\n// same result using `flatMap` (Haskell's `\u003e\u003e=`).\nfunc popTwice() -\u003e State\u003cStack, [Int]\u003e {\n    return pop().flatMap { a in\n           pop().flatMap { b in\n             yield([a, b])\n           }}\n}\n\n// Run `pop()` twice, yielding the second item on the stack. The `then()`\n// method chains a statefule computation onto the first.\nfunc popSecond() -\u003e State\u003cStack, Int\u003e {\n    return pop().then(pop)\n}\n\nlet stack = [1, 2, 3]\n\n// Use `eval()` to access the result of the stateful computation (e.g., the `Int` in `State\u003cStack, Int\u003e`)\npopSecond.eval(stack) // =\u003e 2\n\n// Use `exec()` to run the stateful computation and return the mutated state\npopTwice.exec(stack) // =\u003e [3]\n\n// Use `run()` to get a tuple containing both the result and the final state\nlet (result, state) = pop().run(stack) // result = 1, state = [2, 3]\n```\n\n\n## What this *could* look like with some [useful operators](https://github.com/sharplet/State/issues/7)\n\nThe `|\u003e` operator is an alias for `run()`, and `\u003e\u003e\u003e` is an alias for `then()`.\n\n```swift\nlet (second, _) = [1,2,3] |\u003e pop \u003e\u003e\u003e pop\n// second = 2\n```\n\n\n## Why this is probably not all that useful in practice (or \"Just use `var`\")\n\nThat last example is semantically pretty much identical to this code:\n\n```swift\nstruct Stack\u003cT\u003e {\n    mutating func pop() -\u003e T {\n        return elements.removeLast()\n    }\n\n    mutating func push(element: T) {\n        elements.append(element)\n    }\n\n    private var elements: [T]\n}\n\nfunc popTwice(var stack: Stack) -\u003e [Int] {\n    let a = stack.pop()\n    let b = stack.pop()\n    return [a, b]\n}\n```\n\nSo why wouldn't you just use `var`?\n\nTurns out:\n\n\u003cblockquote class=\"twitter-tweet\" lang=\"en\"\u003e\u003cp\u003e\u003ca href=\"https://twitter.com/jspahrsummers\"\u003e@jspahrsummers\u003c/a\u003e \u0026#39;\u0026quot;var\u0026quot; is the state monad\u0026#39;\u003c/p\u003e\u0026mdash; Joe Groff (@jckarter) \u003ca href=\"https://twitter.com/jckarter/status/510582940158291969\"\u003eSeptember 13, 2014\u003c/a\u003e\u003c/blockquote\u003e \u003cscript async src=\"//platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n\n¯\\\\\\_(ツ)\\_/¯\n\n\n## Installation\n\nUsing [Carthage](https://github.com/Carthage/Carthage):\n\n  - Add this line to your `Cartfile`:\n\n        github \"sharplet/State\"\n\n  - Run `carthage update`\n\n  - Add `State.framework` (located in the `Carthage/Build` directory) to your project (see [here](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) for more detailed instructions)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsharplet%2Fstate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsharplet%2Fstate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsharplet%2Fstate/lists"}