{"id":13800421,"url":"https://github.com/athanclark/purescript-queue","last_synced_at":"2026-02-06T17:33:35.722Z","repository":{"id":58225474,"uuid":"99389069","full_name":"athanclark/purescript-queue","owner":"athanclark","description":"A queue-esque data type for purescript.","archived":false,"fork":false,"pushed_at":"2020-04-19T22:16:51.000Z","size":96,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-20T01:13:54.182Z","etag":null,"topics":["async","handler","pubsub","purescript","purescript-queue"],"latest_commit_sha":null,"homepage":null,"language":"PureScript","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/athanclark.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":"2017-08-05T00:35:49.000Z","updated_at":"2020-04-19T22:16:53.000Z","dependencies_parsed_at":"2022-08-31T03:24:14.395Z","dependency_job_id":null,"html_url":"https://github.com/athanclark/purescript-queue","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/athanclark/purescript-queue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Fpurescript-queue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Fpurescript-queue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Fpurescript-queue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Fpurescript-queue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/athanclark","download_url":"https://codeload.github.com/athanclark/purescript-queue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athanclark%2Fpurescript-queue/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260457585,"owners_count":23012334,"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":["async","handler","pubsub","purescript","purescript-queue"],"created_at":"2024-08-04T00:01:12.424Z","updated_at":"2026-02-06T17:33:30.680Z","avatar_url":"https://github.com/athanclark.png","language":"PureScript","readme":"# PureScript-Queue\n\n[![Build Status](https://travis-ci.org/athanclark/purescript-queue.svg?branch=master)](https://travis-ci.org/athanclark/purescript-queue)\n[![Pursuit](https://pursuit.purescript.org/packages/purescript-queue/badge)](https://pursuit.purescript.org/packages/purescript-queue)\n\nThe pub/sub model captures a simple concept - assign \"handlers\" to an event manager of\nsome kind, and delegate messages to those handlers by issuing events. This is also considered\na \"channel\" in Haskell - something that stores messages until they are read.\n\nThe underlying implementations are pretty simple, because JavaScript is single-threaded. As a\nresult, we have the following architecture:\n\n```purescript\nimport Queue (Queue, readOnly, writeOnly, READ, WRITE, new, on, put)\n\n\nmain :: Effect Unit\nmain = do\n  (q :: Queue (read :: READ, write :: WRITE) Int) \u003c- new\n  \n  -- assign a handler\n  on (readOnly q) logShow\n  \n  -- put messages to the queue\n  put (writeOnly q) 1\n  put q 2 -- Doesn't need to be strictly write-only\n```\n\n\u003e The calls to `readOnly` and `writeOnly` aren't necessary; they're just to demonstrate\n\u003e the ability to quarantine sections of your code, in which you only which to expose\n\u003e `(read :: READ)` or `(write :: WRITE)` access, as the only facilities available to the queue.\n\nThe _primary purpose_ of a queue is to decouple function invocation from parameter application -\nif I don't have a function yet, but I have inputs for it, then I'll just write them to the queue. If\nI have a function, but no inputs yet, then I'll just add the handler to the queue to await inputs.\n_Furthermore_, if you want to _remove_ a handler, you should be able to.\n\nIt tries to imitate similar functionality to `Chan`s from Haskell, but isn't nearly as cool; the\n`IO` monad in Haskell can block indefinitely while other threads write to the same channel, while our\n`Queue`s can only do that in the `Aff` monad (single-threaded, but asynchronous). This functionality\nis achieved using the `Queue.Aff` module and its siblings.\n\nThere are three flavors of `Queue`, sorted by their module:\n\n- `module Queue.One` - the simplest version - in only allows for at-most _one_ handler - useful when you\n  know there's only going to be one listener to the queue, but you don't know when the input will be\n  available.\n- `module Queue` - similar to `Queue.One`, except it allows for a _set_ of handlers at a time - you may\n  _additively_ include individual handlers at any time, but because they can't be distinguished from each\n  other as values (`Function` doesn't implement `Eq`), you can only delete _all of them at once_. This may\n  be useful when you know in-advance that you won't be deleting any handlers.\n- `module IxQueue` - similar to `Queue`, except it makes you _index_ your handlers by some `String`. This\n  _names_ them, and allows you to delete individual handlers at any time without disrupting other ones.\n\n\n## Verbiage\n\n- `new` creates a new, empty queue.\n- `put` inserts data into the queue. If there's at least one listener, it will supply that data to it.\n  If not, it will be cached in-order (oldest to youngest).\n- `draw` takes the oldest data from the queue, in `Aff`. \n- `on` assigns a handler that doesn't die by itself.\n- `once` assigns a handler that dies after receiving _one_ input.\n\nSome less important functions:\n\n- `take` removes the oldest message, without invoking a handler.\n- `pop` removes the youngest message, without invoking a handler.\n- `drain` adds a handler that does nothing - just empties any residual data.\n- `putMany` adds multiple values by traversing the handlers over the container.\n- `takeMany` and `popMany` returns an array of values.\n- `del` removes all handlers from the queue, but not affecting the cache if there is one.\n- `read` observes the values in the queue, without affecting it's cache.\n- `length` returns the length of the cache.\n\n\n### Read and Write Scope\n\nInitially, when using `new`, a queue is both read and write accessible, through the type-level flags `READ` and `WRITE`.\nThis may be undesirable for complex networks of queues, where one section of code clearly only supplies data, while another\none clearly only consumes it. There are functions for changing this:\n\n- `readOnly` removes the `write :: WRITE` label from the row type\n- `writeOnly` removes the `read :: READ` label from the row type\n- `allowReading` adds the `read :: READ` label back to the row type\n- `allowWriting` adds the `write :: WRITE` label back to the row type\n\nThis makes the type signature for a queue look something like `Queue (read :: READ, write :: WRITE) Foo`.\n\n\n### Extra\n\nThere is also some extra kit defined in `module Queue.Types` - `debounceStatic`, `throttleStatic`, and `intersperseStatic`.\nThese functions take similar arguments - some _time_ value, and a _readable_ queue, and return a _writable_ queue, with some _thread_ (fiber).\n\nGenerally, in your code, you will be _listening_ to the queue you provide, while writing to the queue these functions return, to get\ntheir intended effects.\n\n\n- `debounceStatic` is intended to _drop_ messages sent before the time parameter - useful for user interface applications.\n- `throttleStatic` is intended to _delay_ all messages sent before the time parameter, without loss. Useful for network applications.\n- `intersperseStatic` is intended to _create_ messages if none are sent after the time parameter, without loss. Useful for\n  connectivity in network applications (pings).\n\n\n\u003e Note: these variants are called \"fooStatic\" because there's only a single time value used, and wouldn't be capable of something\n\u003e more advanced like exponential falloff.\n\n\n## Asynchronous message plumbing\n\nThis library's additional goal was to aid in asynchronous interop; having some source data originate asynchronously,\nand the ability to handle it spontaneously. Through the `IOQueues` and `IxQueue.IOQueues` modules, we can treat\nmessage passing to queues at a higher level, as _procedure invocations_. We do this by creating _two_ queues - one\nfor handling inputs to the handler(s), and one for returning results from the handler(s).\n\nIn the `module IOQueues` and `module IxQueue.IOQueues` modules, there's some somewhat confusing nomenclature:\n\n- `new` creates an `IOQueues` - a pair of queues; one for input, one for output.\n- `callAsync` puts an input in the `IOQueues`, and blocks until an output is available in `Aff`.\n- `callAsyncEff` does the same thing as `callAsync`, but can't block in `Aff` and only operates in `Effect`.\n- `registerSync` attaches a processing function to the `IOQueues`, taking an input and returning an output,\n  but does so in lock-step - atomically adding a function to the system synchronously.\n- `registerSyncOnce` does the same thing as `registerSync`, but removes itself after being invoked once.\n\n\n```purescript\nimport Queue.One (Queue) as One\n-- ^ most lightweight implementation, only one handler\nimport IOQueues (new, registerSync, callAsync, IOQueues)\nimport Effect.Aff (runAff_)\n\nmain = do\n  (io :: IOQueues One.Queue Int Int) \u003c- newIOQueues\n  -- \"IOQueues queue input output\" means \"using 'queue', take 'input' and make 'output'.\"\n  \n  let handler :: Int -\u003e Effect Int\n      handler i = do\n        log $ \"input: \" \u003c\u003e show i\n        let o = i + 1\n        log $ \"incremented: \" \u003c\u003e show o\n        pure o\n  registerSync io handler -- attach the handler in Effect\n    \n  -- `resolveAff` does nothing - it's needed by `runAff_` - see `Effect.Aff` for details\n  let resolveAff :: Either String Unit -\u003e Effect Unit\n      resolveAff _ = pure unit\n\n  runAff_ resolveAff do\n    result \u003c- callAsync io 20 -- invoke delegated computation in Aff\n    liftEffect $ log $ \"Should be 21 - Result: \" \u003c\u003e show result\n```\n\n### Multiple Handlers\n\n`module IxQueue.IOQueues` is useful when you need async `IOQueues` calls, but need to integrate into an\nexisting `IxQueue` with a network of handlers and data supplied. The primary difference with this is that\nthrough `IxQueue.IOQueues`, you need to pass a `String` reference to identify which handlers will be targeted\nby what data.\n\n\nUsing a `module Queue` as an underlying `IOQueues` queue might be pretty confusing - it allows for multiple\n`registerSync` handlers waiting for the same input, which would cause a race condition for the `callAsync`\ninvocation. However, if you mess with the internal output queue in the `IOQueues` and add extra handlers,\nyou may broadcast the results of the `registerSync` handler to multiple areas, without race conditions\n(`callAsync` only reads from the output queue once). Either way, this use case would probably not be stable\nor in your favor, and should generally be avoided.\n\n\n# Contributing\n\nThis library has grown quite a lot over the years, and I still find it very useful.\n[purescript-react-queue](https://pursuit.purescript.org/packages/purescript-react-queue) was spawned from this,\nand I still use it all the time for managing react components.\n\nIf you have any ideas you'd like to see in this library, please file an issue or drop me a line. Thanks for using\nit!\n","funding_links":[],"categories":["Asynchronicity and Parallelism"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathanclark%2Fpurescript-queue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fathanclark%2Fpurescript-queue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathanclark%2Fpurescript-queue/lists"}