{"id":16348828,"url":"https://github.com/mstksg/mutable","last_synced_at":"2025-03-16T15:31:15.107Z","repository":{"id":56847767,"uuid":"234800057","full_name":"mstksg/mutable","owner":"mstksg","description":"Automatic piecewise-mutable references for your types","archived":false,"fork":false,"pushed_at":"2020-07-10T15:28:40.000Z","size":916,"stargazers_count":42,"open_issues_count":4,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-26T20:22:28.583Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://mutable.jle.im","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mstksg.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-01-18T21:26:25.000Z","updated_at":"2025-01-24T15:43:46.000Z","dependencies_parsed_at":"2022-09-09T06:23:34.702Z","dependency_job_id":null,"html_url":"https://github.com/mstksg/mutable","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mstksg%2Fmutable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mstksg%2Fmutable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mstksg%2Fmutable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mstksg%2Fmutable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mstksg","download_url":"https://codeload.github.com/mstksg/mutable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243822312,"owners_count":20353496,"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-11T00:55:17.756Z","updated_at":"2025-03-16T15:31:14.833Z","avatar_url":"https://github.com/mstksg.png","language":"Haskell","readme":"[mutable][docs]\n===============\n\n[![mutable on Hackage](https://img.shields.io/hackage/v/mutable.svg?maxAge=86400)](https://hackage.haskell.org/package/mutable)\n[![Build Status](https://travis-ci.org/mstksg/mutable.svg?branch=master)](https://travis-ci.org/mstksg/mutable)\n\n**[Documentation and Walkthrough][docs]**\n\n[docs]: https://mutable.jle.im\n\n**[Introductory Blog Post][blog]**\n\n[blog]: https://blog.jle.im/entry/introducing-the-mutable-library.html\n\nBeautiful Mutable Values\n------------------------\n\n**Mutability can be awesome!**\n\nTake back the power of **mutable objects** with all the **safety** and explicit\nstate of Haskell. Associate and generate \"piecewise-mutable\" versions for your\ncomposite data types in a composable and automatic way.  Think of it like a\n\"generalized `MVector` for all ADTs\".  It also leverages GHC Generics to make\nworking with piecewise mutability as simple as possible.\n\nMaking piecewise updates on your giant composite data types (like artificial\nneural networks or game states in your game loop) got you down because they\nrequire re-allocating the entire value?  Tired of requiring a full deep copy\nevery time you make a small change, and want to be able to build mutable\nversions of your types automatically in composable ways? This is the package\nfor you.\n\nUseful for a situation where you have a record with many fields (or many nested\nrecords) that you want to use for efficient mutable in-place algorithms.  This\nlibrary lets you do efficient \"piecewise\" mutations (operations that only edit\none field), and also efficient entire-datatype copies/updates, as well, in many\ncases.\n\nCheck out the [documentation home page][docs], [haddock reference][haddock],\n[introductory blog post on insights and lessons learned][blog], or read below\nfor motivation and high-level descriptions.\n\n[haddock]: https://hackage.haskell.org/package/mutable\n\nMotivation\n----------\n\n### Piecewise-Mutable\n\nFor a simple motivating example where in-place piecewise mutations might be\nbetter, consider a large vector.\n\nLet's say you only want to edit the first item in a vector, multiple times.\nThis is extremely inefficient with a pure vector:\n\n```haskell\naddFirst :: Vector Double -\u003e Vector Double\naddFirst xs = iterate incr xs !! 1000000\n  where\n    incr v = v V.// [(0, (v V.! 0) + 1)]\n```\n\nThat's because `addFirst` will copy over the entire vector for every step\n--- every single item, even if not modified, will be copied one million times.\nIt is `O(n*l)` in memory updates --- it is very bad for long vectors or large\nmatrices.\n\nHowever, this is extremely efficient with a mutable vector:\n\n```haskell\naddFirst :: Vector Double -\u003e Vector Double\naddFirst xs = runST $ do\n    v \u003c- V.thaw xs\n    replicateM_ 1000000 $ do\n        MV.modify v 0 (+ 1)\n    V.freeze v\n```\n\n(running this in `ST`, the mutable memory monad that comes with GHC)\n\nThis is because all of the other items in the vector are kept the same and not\ncopied-over over the course of one million updates.  It is `O(n+l)` in memory\nupdates.  It is very good even for long vectors or large matrices.\n\n(Of course, this situation is somewhat contrived, but it isolates a problem that\nmany programs face.  A more common situation might be that you have two\nfunctions that each modify different items in a vector in sequence, and you\nwant to run them many times interleaved, or one after the other.)\n\n### Composite Datatype\n\nThat all works for `MVector`, but let's say you have a simple composite data\ntype that is two vectors:\n\n```haskell\ndata TwoVec = TV { tv1 :: Vector Double\n                 , tv2 :: Vector Double\n                 }\n  deriving Generic\n```\n\nIs there a nice \"piecewise-mutable\" version of this?  You *could* break up\n`TwoVec` manually into its pieces and treat each piece independently, but that method\nisn't composable.  If only there was some equivalent of `MVector` for\n`TwoVec`...and some equivalent of `MV.modify`.\n\nThat's where this library comes in.\n\n```haskell\ninstance Mutable s TwoVec where\n    type Ref s TwoVec = GRef s TwoVec\n```\n\nThis gives us `thawRef :: TwoVec -\u003e m (GRef s TwoVec)`, where `GRef s TwoVec`\nis a mutable version of `TwoVec`, like how `MVector s Double` is a mutable\nversion of `Vector Double`.  It stores each field `tv1` and `tv2` as a seaprate\n`MVector` in memory that can be modified independently.\n\nNow we can write:\n\n```haskell\naddFirst :: TwoVec -\u003e TwoVec\naddFirst xs = runST $ do\n    v \u003c- thawRef xs\n    replicateM_ 1000000 $ do\n      withField #tv1 v $ \\u -\u003e\n        MV.modify u 0 (+ 1)\n    freezeRef v\n```\n\nThis will in-place edit only the first item in the `tv1` field one million\ntimes, without ever needing to copy over the contents `tv2`.  Basically, it\ngives you a version of `TwoVec`  that you can modify in-place piecewise.  You\ncan compose two functions that each work piecewise on `TwoVec`:\n\n```haskell\nmut1 :: Ref s TwoVec -\u003e ST s ()\nmut1 v = do\n    withField #tv1 v $ \\u -\u003e\n      MV.modify u 0 (+ 1)\n      MV.modify u 1 (+ 2)\n    withField #tv2 v $ \\u -\u003e\n      MV.modify u 2 (+ 3)\n      MV.modify u 3 (+ 4)\n\nmut2 :: Ref s TwoVec -\u003e ST s ()\nmut2 v = do\n    withField #tv1 v $ \\u -\u003e\n      MV.modify u 4 (+ 1)\n      MV.modify u 5 (+ 2)\n    withField #tv2 v $ \\u -\u003e\n      MV.modify u 6 (+ 3)\n      MV.modify u 7 (+ 4)\n\ndoAMillion :: TwoVec -\u003e TwoVec\ndoAMillion xs = runST $ do\n    v \u003c- thawRef xs\n    replicateM_ 1000000 $ do\n      mut1 v\n      mut2 v\n    freezeRef v\n```\n\nThis is a type of composition and interleaving that cannot be achieved by\nsimply breaking down `TwoVec` and running functions that work purely on each of\nthe two vectors individually.\n\nMutable Sum Types\n-----------------\n\nThere is also support for mutable sum types, as well.  Here is the automatic\ndefinition of a *[mutable linked list][ll]*:\n\n[ll]: https://en.wikipedia.org/wiki/Linked_list\n\n```haskell\ndata List a = Nil | Cons a (List a)\n  deriving (Show, Generic)\ninfixr 5 `Cons`\n\ninstance Mutable s a =\u003e Mutable s (List a) where\n    type Ref s (List a) = GRef s (List a)\n```\n\nWe can write a function to \"pop\" out the top value and shift the rest of the\nlist up:\n\n```haskell\npopStack\n    :: Mutable s a\n    =\u003e Ref s (List a)\n    -\u003e ST s (Maybe a)\npopStack xs = do\n    c \u003c- projectBranch (constrMB #_Cons) xs\n    forM c $ \\(y, ys) -\u003e do\n      o \u003c- freezeRef y\n      moveRef xs ys\n      pure o\n```\n\n```haskell\nghci\u003e runST $ do\n    r \u003c- thawRef $ 1 `Cons` 2 `Cons` 3 `Cons` Nil\n    y \u003c- popStack r\n    (y,) \u003c$\u003e freezeRef r\n-- =\u003e (Just 1, 2 `Cons` 3 `Cons` Nil)\n```\n\nShow me the numbers\n-------------------\n\nHere are some benchmark cases --- only bars of the same color are comparable,\nand shorter bars are better (performance-wise).\n\n![Benchmarks](https://i.imgur.com/S95TuiM.png)\n\nThere are four situations here, compared and contrasted between pure and\nmutable versions\n\n1.  A large ADT with 256 fields, generated by repeated nestings of `data V4 a =\n    V4 !a !a !a !a`\n\n    1.  Updating only a single part (one field out of 256)\n    2.  Updating the entire ADT (all 256 fields)\n\n2.  A composite data type of four `Vector`s of 500k elements each, so 2 million\n    elements total.\n\n    1.  Updating only a single part (one item out of 2 million)\n    2.  Updating all elements of all four vectors (all 2 million items)\n\nWe can see four conclusions:\n\n1.  For a large ADT, updating a single field (or multiple fields, interleaved)\n    is going to be faster with *mutable*.  This speedup is between x4 and x5,\n    suggesting it is a speedup arising from the fact that the top-level type\n    has four fields.\n2.  For a large ADT, updating the whole ADT (so just replacing the entire\n    thing, no actual copies) is faster just as a pure value by a large factor\n    (which is a big testament to GHC).\n3.  For a small ADT with huge vectors, updating a single field is *much* faster\n    with *mutable*.\n4.  For a small ADT with huge vectors, updating the entire value (so, the\n    entire vectors and entire ADT) is actually faster with *mutable* as well.\n\nInterestingly, the \"update entire structure\" case (which should be the\nworst-case for *mutable* and the best-case for pure values) actually becomes\nfaster with *mutable* when you get to the region of *many* values... somewhere\nbetween 256 and 2 million, apparently.  However, this may just be from the\nefficiency of modifying vectors sequentially.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmstksg%2Fmutable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmstksg%2Fmutable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmstksg%2Fmutable/lists"}