{"id":23033677,"url":"https://github.com/spl/dlist","last_synced_at":"2025-04-06T02:08:55.473Z","repository":{"id":390473,"uuid":"14329199","full_name":"spl/dlist","owner":"spl","description":"Difference lists in Haskell","archived":false,"fork":false,"pushed_at":"2024-07-04T12:33:34.000Z","size":234,"stargazers_count":65,"open_issues_count":8,"forks_count":16,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-31T13:57:23.219Z","etag":null,"topics":["data-structures","difference-lists","haskell"],"latest_commit_sha":null,"homepage":"https://hackage.haskell.org/package/dlist","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/spl.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license.md","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":"2013-11-12T10:35:22.000Z","updated_at":"2024-08-03T19:45:01.000Z","dependencies_parsed_at":"2022-07-17T00:00:39.240Z","dependency_job_id":"2ecfb851-e0b5-4d1c-b47a-fbfb19ac2002","html_url":"https://github.com/spl/dlist","commit_stats":{"total_commits":245,"total_committers":23,"mean_commits":"10.652173913043478","dds":"0.23673469387755097","last_synced_commit":"8ce1a1332e84f177d5303fcd429a0aead8c928d9"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spl%2Fdlist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spl%2Fdlist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spl%2Fdlist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spl%2Fdlist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spl","download_url":"https://codeload.github.com/spl/dlist/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247423515,"owners_count":20936626,"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":["data-structures","difference-lists","haskell"],"created_at":"2024-12-15T16:17:25.796Z","updated_at":"2025-04-06T02:08:55.452Z","avatar_url":"https://github.com/spl.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Difference Lists\n\n[![test-badge][]][test]\n[![hackage-badge][]][hackage-dlist]\n[![packdeps-badge][]][packdeps]\n\n_**List-like types supporting O(1) `append` and `snoc` operations.**_\n\n## Installation\n\n[`dlist`][hackage-dlist] is a Haskell package available from [Hackage][hackage].\nIt can be installed with [`cabal`][cabal] or [`stack`][stack].\n\nSee the [change log][changelog] for the changes in each version.\n\n## Usage\n\nHere is an example of “flattening” a `Tree` into a list of the elements in its\n`Leaf` constructors:\n\n```haskell\nimport qualified Data.DList as DList\n\ndata Tree a = Leaf a | Branch (Tree a) (Tree a)\n\nflattenSlow :: Tree a -\u003e [a]\nflattenSlow = go\n  where\n    go (Leaf x) = [x]\n    go (Branch left right) = go left ++ go right\n\nflattenFast :: Tree a -\u003e [a]\nflattenFast = DList.toList . go\n  where\n    go (Leaf x) = DList.singleton x\n    go (Branch left right) = go left `DList.append` go right\n```\n\n(The above code can be found in the [benchmark][].)\n\n`flattenSlow` is likely to be slower than `flattenFast`:\n\n1. `flattenSlow` uses `++` to concatenate lists, each of which is recursively\n   constructed from the `left` and `right` `Tree` values in the `Branch`\n   constructor.\n\n2. `flattenFast` does not use `++` but constructs a composition of functions,\n   each of which is a “cons” introduced by `DList.singleton` (`(x :)`). The\n   function `DList.toList` applies the composed function to `[]`, constructing\n   a list in the end.\n\nTo see the difference between `flattenSlow` and `flattenFast`, consider some\nrough evaluations of the functions applied to a `Tree`:\n\n```haskell\nflattenSlow (Branch (Branch (Leaf 'a') (Leaf 'b')) (Leaf 'c'))\n  = go (Branch (Branch (Leaf 'a') (Leaf 'b')) (Leaf 'c'))\n  = go (Branch (Leaf 'a') (Leaf 'b')) ++ go (Leaf 'c')\n  = (go (Leaf 'a') ++ go (Leaf 'b')) ++ \"c\"\n  = (\"a\" ++ \"b\") ++ \"c\"\n  = ('a' : [] ++ \"b\") ++ \"c\"\n  = ('a' : \"b\") ++ \"c\"\n  = 'a' : \"b\" ++ \"c\"\n  = 'a' : 'b' : [] ++ \"c\"\n  = 'a' : 'b' : \"c\"\n```\n\n```haskell\nflattenFast (Branch (Branch (Leaf 'a') (Leaf 'b')) (Leaf 'c'))\n  = toList $ go (Branch (Branch (Leaf 'a') (Leaf 'b')) (Leaf 'c'))\n  = toList $ go (Branch (Leaf 'a') (Leaf 'b')) `append` go (Leaf 'c')\n  = unsafeApplyDList (go (Branch (Leaf 'a') (Leaf 'b'))) . unsafeApplyDList (go (Leaf 'c')) $ []\n  = unsafeApplyDList (go (Branch (Leaf 'a') (Leaf 'b'))) (unsafeApplyDList (go (Leaf 'c')) [])\n  = unsafeApplyDList (go (Branch (Leaf 'a') (Leaf 'b'))) (unsafeApplyDList (singleton 'c') [])\n  = unsafeApplyDList (go (Branch (Leaf 'a') (Leaf 'b'))) (unsafeApplyDList (UnsafeDList ((:) 'c')) [])\n  = unsafeApplyDList (go (Branch (Leaf 'a') (Leaf 'b'))) \"c\"\n  = unsafeApplyDList (UnsafeDList (unsafeApplyDList (go (Leaf 'a')) . unsafeApplyDList (go (Leaf 'b')))) \"c\"\n  = unsafeApplyDList (go (Leaf 'a')) (unsafeApplyDList (go (Leaf 'b')) \"c\")\n  = unsafeApplyDList (go (Leaf 'a')) (unsafeApplyDList (singleton 'b') \"c\")\n  = unsafeApplyDList (go (Leaf 'a')) (unsafeApplyDList (UnsafeDList ((:) 'b')) \"c\")\n  = unsafeApplyDList (go (Leaf 'a')) ('b' : \"c\")\n  = unsafeApplyDList (singleton 'a') ('b' : \"c\")\n  = unsafeApplyDList (UnsafeDList ((:) 'a')) ('b' : \"c\")\n  = 'a' : 'b' : \"c\"\n```\n\nThe left-nested `++` in `flattenSlow` results in intermediate list constructions\nthat are immediately discarded in the evaluation of the outermost `++`. On the\nother hand, the evaluation of `flattenFast` involves no intermediate list\nconstruction but rather function applications and `newtype` constructor wrapping\nand unwrapping. This is where the efficiency comes from.\n\n_**Warning!**_ Note that there is truth in the above, but there is also a lot of\nhand-waving and intrinsic complexity. For example, there may be GHC rewrite\nrules that apply to `++`, which will change the actual evaluation. And, of\ncourse, strictness, laziness, and sharing all play a significant role. Also, not\nevery function in the `dlist` package is the most efficient for every situation.\n\n_**Moral of the story:**_ If you are using `dlist` to speed up your code, check\nto be sure that it actually does. Benchmark!\n\n## Design Notes\n\nThese are some notes on design and development choices made for the `dlist`\npackage.\n\n### Avoid `++`\n\nThe original intent of Hughes' representation of lists as first-class functions\nwas to provide an abstraction such that the list `append` operation found in\nfunctional programming languages (and now called `++` in Haskell) would not\nappear in left-nested positions to avoid duplicated structure as lists are\nconstructed. The lesson learned by many people using list over the years is that\nthe `append` operation can appear, sometimes surprisingly, in places they don't\nexpect it.\n\nOne of our goals is for the `dlist` package to avoid surprising its users with\nunexpected insertions of `++`. Towards this end, there should be a minimal set\nof functions in `dlist` in which `++` can be directly or indirectly found. The\nlist of known uses of `++` includes:\n\n* `DList`: `fromList`, `fromString`, `read`\n* `DNonEmpty`: `fromList`, `fromNonEmpty`, `fromString`, `read`\n\nIf any future requested functions involve `++` (e.g. via `fromList`), the burden\nof inclusion is higher than it would be otherwise.\n\n### Abstraction\n\nThe `DList` representation and its supporting functions (e.g. `append`, `snoc`,\netc.) rely on an invariant to preserve its safe use. That is, without this\ninvariant, a user may encounter unexpected outcomes.\n\n(We use safety in the sense that the semantics are well-defined and expected,\nnot in the sense of side of referential transparency. The invariant does not\ndirectly lead to side effects in the `dlist` package, but a program that uses an\nunsafely generated `DList` may do something surprising.)\n\nThe invariant is that, for any `xs :: DList a`:\n\n```haskell\nfromList (toList xs) = xs\n```\n\nTo see how this invariant can be broken, consider this example:\n\n```haskell\nxs :: DList a\nxs = UnsafeDList (const [])\n\nfromList (toList (xs `snoc` 1))\n  = fromList (toList (UnsafeDList (const []) `snoc` 1))\n  = fromList (toList (UnsafeDList (unsafeApplyDList (UnsafeDList (const [])) . (x :))))\n  = fromList (toList (UnsafeDList (const [] . (x :))))\n  = fromList (($ []) . unsafeApplyDList $ UnsafeDList (const [] . (x :)))\n  = fromList (const [] . (x :) $ [])\n  = fromList (const [] [x])\n  = fromList []\n  = UnsafeDList (++ [])\n```\n\nThe invariant can also be stated as:\n\n```haskell\ntoList (fromList (toList xs)) = toList xs\n```\n\nAnd we can restate the example as:\n\n```haskell\ntoList (fromList (toList (xs `snoc` 1)))\n  = toList (UnsafeDList (++ []))\n  = []\n```\n\nIt would be rather unhelpful and surprising to find ``(xs `snoc` 1)`` turned out\nto be the empty list.\n\nTo preserve the invariant on `DList`, we provide it as an abstract type in the\n`Data.DList` module. The constructor, `UnsafeDList`, and record label,\n`unsafeApplyDList`, are not exported because these can be used, as shown above,\nto break the invariant.\n\nAll of that said, there have been numerous requests to export the `DList`\nconstructor. We are not convinced that it is necessary, but we are convinced\nthat users should decide for themselves.\n\nTo use the constructor and record label of `DList`, you import them as follows:\n\n```haskell\nimport Data.DList.Unsafe (DList(UnsafeDList, unsafeApplyDList))\n```\n\nIf you are using Safe Haskell, you may need to add this at the top of your\nmodule:\n\n```haskell\n{-# LANGUAGE Trustworthy #-}\n```\n\nJust be aware that the burden of proof for safety is on you.\n\n## References\n\nThese are various references where you can learn more about difference lists.\n\n### Research\n\n* **A novel representation of lists and its application to the function\n  “reverse.”** John Hughes. Information Processing Letters. Volume 22, Issue 3.\n  1986-03. Pages 141-144. [PDF][hughes-pdf]\n\n  This is the original published source for a representation of lists as\n  first-class functions.\n\n### Background\n\n* [Wikipedia][wikipedia]\n* [Haskell Wiki][wiki-haskell]\n* [Stack Overflow][stack-overflow]\n\n### Blogs and Mailing Lists\n\n* [Using Difference Lists][blog-auclair-1]. Douglas M. Auclair. 2008-08-13.\n* [A Sort of Difference][blog-kmett]. Edward Kmett. 2008-09-18.\n* [Reference for technique wanted][mail-okeefe]. Richard O'Keefe, et al.\n  2010-10-31.\n* [24 Days of Hackage: dlist][blog-charles]. Oliver Charles. 2012-12-14.\n* [Constructing a list in a Monad][blog-breitner]. Joachim Breitner. 2013-11-13.\n* [Demystifying DList][blog-ellis] ([Reddit][blog-ellis-reddit]). Tom Ellis.\n  2014-01-24.\n* [keepEquals with Difference Lists][blog-auclair-2]. Douglas M. Auclair.\n  2014-06-21.\n\n### Books\n\n* [Chapter 13. Data Structures][book-real-world-haskell]. Real World Haskell.\n  2008-12-05.\n\n## License\n\n[BSD 3-Clause “New” or “Revised” License][license] © Don Stewart, Sean Leather,\ncontributors\n\n[changelog]: https://github.com/spl/dlist/blob/main/changelog.md#change-log\n[benchmark]: https://github.com/spl/dlist/blob/main/bench/Main.hs\n[blog-auclair-1]: https://logicaltypes.blogspot.com/2008/08/using-difference-lists.html\n[blog-auclair-2]: https://logicaltypes.blogspot.com/2014/06/keepequals-with-difference-lists.html\n[blog-breitner]: https://www.joachim-breitner.de/blog/620-Constructing_a_list_in_a_Monad\n[blog-charles]: https://ocharles.org.uk/blog/posts/2012-12-14-24-days-of-hackage-dlist.html\n[blog-ellis-reddit]: https://www.reddit.com/r/haskell/comments/1w5duf/demystifying_dlist/\n[blog-ellis]: http://h2.jaguarpaw.co.uk/posts/demystifying-dlist/\n[blog-kmett]: https://web.archive.org/web/20080918101635/comonad.com/reader/2008/a-sort-of-difference/\n[book-real-world-haskell]: http://book.realworldhaskell.org/read/data-structures.html\n[cabal]: https://cabal.readthedocs.io/\n[hackage-badge]: https://img.shields.io/hackage/v/dlist.svg?maxAge=3600\n[hackage-dlist]: https://hackage.haskell.org/package/dlist\n[hackage]: https://hackage.haskell.org/\n[hughes-pdf]: https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/lists.pdf\n[license]: https://github.com/spl/dlist/blob/main/license.md\n[mail-okeefe]: https://www.mail-archive.com/haskell-cafe@haskell.org/msg83699.html\n[packdeps-badge]: https://img.shields.io/hackage-deps/v/dlist.svg?maxAge=3600\n[packdeps]: http://packdeps.haskellers.com/feed?needle=dlist\n[stack-overflow]: https://stackoverflow.com/questions/3352418/what-is-a-dlist\n[stack]: https://docs.haskellstack.org/\n[test-badge]: https://github.com/spl/dlist/actions/workflows/haskell-ci.yml/badge.svg\n[test]: https://github.com/spl/dlist/actions/workflows/haskell-ci.yml\n[wiki-haskell]: https://wiki.haskell.org/Difference_list\n[wikipedia]: https://en.wikipedia.org/wiki/Difference_list\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspl%2Fdlist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspl%2Fdlist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspl%2Fdlist/lists"}