{"id":13801235,"url":"https://github.com/dm3/manifold-cljs","last_synced_at":"2025-07-27T03:34:40.965Z","repository":{"id":62433527,"uuid":"75775287","full_name":"dm3/manifold-cljs","owner":"dm3","description":"Manifold implementation in Clojurescript","archived":false,"fork":false,"pushed_at":"2019-12-20T14:59:52.000Z","size":62,"stargazers_count":48,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-01T17:36:23.801Z","etag":null,"topics":["async","cljs","clojurescript","manifold","rx","stream"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dm3.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":"2016-12-06T22:00:02.000Z","updated_at":"2024-11-06T14:45:50.000Z","dependencies_parsed_at":"2022-11-01T20:45:49.961Z","dependency_job_id":null,"html_url":"https://github.com/dm3/manifold-cljs","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/dm3/manifold-cljs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dm3%2Fmanifold-cljs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dm3%2Fmanifold-cljs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dm3%2Fmanifold-cljs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dm3%2Fmanifold-cljs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dm3","download_url":"https://codeload.github.com/dm3/manifold-cljs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dm3%2Fmanifold-cljs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267294177,"owners_count":24065343,"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","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","cljs","clojurescript","manifold","rx","stream"],"created_at":"2024-08-04T00:01:20.788Z","updated_at":"2025-07-27T03:34:35.944Z","avatar_url":"https://github.com/dm3.png","language":"Clojure","funding_links":[],"categories":["Awesome ClojureScript"],"sub_categories":["Reactive Programming"],"readme":"# manifold-cljs\n\n[![Build Status](https://travis-ci.org/dm3/manifold-cljs.png?branch=master)](https://travis-ci.org/dm3/manifold-cljs)\n\nA port of [Manifold](https://github.com/ztellman/manifold) to Clojurescript.\n\nThis port tracks the latest Manifold version as closely as possible. As per\n[Clojurescript port](https://github.com/ztellman/manifold/issues/2) issue, Zach\nwanted to keep the port separate from the main project - so here it is.\nHowever, ~80% of the code was copied from the Clojure Manifold verbatim, so\nthere's a chance it might get ported back with reader conditionals.\n\nThe port hasn't been used in any serious applications yet, but there are some\ntests. And they pass!\n\nThere are no blocking operations in Javascript, so some of the original\nManifold functions had to go.\n\n## Usage\n\nAdd the following dependency to your project.clj or build.boot:\n\n```clojure\n[manifold-cljs \"0.1.7-1\"]\n```\n\nThen use it in your project:\n\n```clojure\n(ns example.project\n  (:require [manifold-cljs.stream :as s]\n            [manifold-cljs.deferred :as d]))\n```\n\nYou can find several examples in the `examples/` directory.\n\n## Extensions\n\n* [Core.Async](https://github.com/clojure/core.async) adapter at [Manifold-cljs.Core.Async](https://github.com/dm3/manifold-cljs.core.async).\n\n## Differences to Clojure implementation\n\n### Executors\n\n`manifold-cljs.executor` defines an `Executor` protocol with implementations\nbacked by `goog.async.nextTick`, `setTimeout`, synchronous execution and a\nbatched variation which takes another executor as its implementation. An\nexecutor is selected while creating a Stream or a Deferred.\n\nThe call to `manifold-cljs.executor/executor` will always return the batched\n`next-tick` executor by default, so all of the callbacks on Streams and\nDeferreds are executed as tasks. Either as microtasks, if `setImmediate` is\navailable, or as tasks (`setTimeout`). This means that the code will behave\nsimilarly to the way it would behave on the JVM if every stream and deferred\nwere executed on an executor.\n\nOn the JVM, the call to `manifold.executor/executor` will return no executor by\ndefault, which will make the callbacks run on whichever thread triggered the\ncompletion of the Deferred.\n\nThe difference becomes apparent in the following snippet:\n\n```clojure\n(let [s (s/stream), b (s/batch 2 s)]\n  (s/put-all! s [1 2 3])\n  (s/close! s)\n  (assert (= [[1 2] [3]] (s/stream-\u003eseq b))))\n```\n\nThe above succeeds when run on a single thread, but fails if run within a\n`(manifold.executor/with-executor (manifold.executor/execute-pool) ...)` block.\nThe second result element - `[3]` - nevers gets delivered to the batched stream\nas the source stream gets closed first. All the `Consumer` events registered on\nthe source get canceled.\n\n### Blocking put/take\n\n`stream/put` and `stream/take` have the same signature as their Clojure\ncounterparts, however setting the `blocking?` parameter to `true` will always\ntrigger an assertion error. Puts and takes in manifold-cljs will always return\na deferred result.  Consequently there is no way to synchronously connect\nstreams and `stream/isSynchronous` always returns false.\n\n### Metadata\n\nI couldn't find a protocol allowing mutation of metadata in Clojurescript. The\ndefault deferred and stream implementations are mutable, so there are no `IMeta`\nor `IWithMeta` implementations for streams/deferreds.\n\n### Deferred protocols\n\nThe protocols for the Deferred have been moved to\n`manifold-cljs.deferred.core`. The default implementation - to\n`manifold-cljs.deferred.default-impl`. This is analogous to what has been done to\nstreams.  This was done in order to avoid a cyclic dependency between\n`manifold-cljs.deferred`, where the protocols used to live, and\n`manifold-cljs.time`. Clojurescript is compiled statically and can't `require`\na namespace in the middle of the file.\n\nIdeally we should propagate this change to the Clojure Manifold.\n\n### Missing functions\n\n* `manifold-cljs.stream/stream-\u003eseq` - inherently blocking\n* `manifold-cljs.deferred`\n    - `let-flow` - TODO: needs some advanced code walking\n* `manifold-cljs.executor`\n    - `instrumented-executor` - TODO: do we want this in Cljs?\n    - `*stats-callbacks*` - TODO: do we want this in Cljs?\n    - `utilization-executor` - not applicable\n    - `execute-pool` - not applicable\n    - `wait-pool` - not applicable\n* `manifold-cljs.time`\n    - `format-duration` - niche\n    - `floor` - niche\n    - `add` - niche\n    - `IClock` - TODO: do we want this in Cljs?\n    - `IMockClock` - TODO: do we want this in Cljs?\n    - `mock-clock` - TODO: do we want this in Cljs?\n    - `*clock*` - TODO: do we want this in Cljs?\n    - `with-clock` - TODO: do we want this in Cljs?\n    - `scheduled-executor-\u003eclock` - not applicable\n* `manifold-cljs.utils`\n    - `without-overflow` - not used\n    - `fast-satisfies` - don't need\n    - `with-lock` - don't need\n\n### Cljs-only functions\n\n* `manifold-cljs.deferred`\n    - `time` - measure time taken to evaluate the body in a deferred\n\n## TODO\n\n* WeakMap dependency - this can somehow be compiled in by the GCC - how?\n* unhandled error reporting - like goog.Deferred/Bluebird\n* DEBUG stack traces - like goog.Deferred/Bluebird\n* better logging - format to dev console?\n* performance - currently ~3x slower than core.async on the `daisy` example\n* `deferred/let-flow` - needs a different deep code walking impl/riddley replacement for Cljs\n\nSee [Closure Promise](https://github.com/google/closure-library/blob/master/closure/goog/promise/promise.js#L84) for more ideas.\n\n## Patterns and Gotchas\n\nManifold is in need of best practices/patterns/gotchas library.\n\n### Writing `d/loop`-based stream combinators\n\nMany of the stream combinators, like `s/zip`, use `d/loop` inside to take from\na source stream and put into the destination stream. There is an additional\nstep needed to make a combinator like that work well when the source stream is\nconnected to other streams as well as combined via the combinator - passing\nvalues through an intermediary stream. See [related Github\nissue](https://github.com/ztellman/manifold/issues/87) for more info.\n\n### Error handling\n\n### Signalling \"no more messages\" upstream\n\n### Upstream is really closed only after an additional put\n\nMost people expect the `s/on-closed` callback to get called once the `s/close!`\nis called on a stream. This is true if the callback is registered on the stream\nthat is being closed. However, in case we `s/close!` a downstream stream and\nthe callback is registered upstream, the close callback will only trigger once\nthe producer tries to put another value into the upstream. See\n[this](https://github.com/ztellman/manifold/issues/82) and\n[this](https://github.com/ztellman/manifold/issues/56) Github issues for more\ninfo.\n\n### `d/let-flow` won't handle deferred conditionals in a smart way\n\nIf you have a conditional clause where the condition is a deferred as well as a\nresult - the result deferred will be awaited even if the condition is falsey,\ne.g.:\n\n```clojure\n(let [x (deferred-never-realized-unless-y)]\n  (let-flow [y (deferred-false)]\n    (if y x :ok))\n```\n\nThe above will block on `x`, even though `y` realizes to `false`.\n\nSee [this](https://github.com/ztellman/manifold/issues/47) Github issue for more info.\n\n## License\n\nCopyright © 2016 Zach Tellman, Vadim Platonov\n\nDistributed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdm3%2Fmanifold-cljs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdm3%2Fmanifold-cljs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdm3%2Fmanifold-cljs/lists"}