{"id":15631608,"url":"https://github.com/borkdude/speculative","last_synced_at":"2025-10-23T21:27:44.950Z","repository":{"id":62434698,"uuid":"153889659","full_name":"borkdude/speculative","owner":"borkdude","description":"Unofficial community-driven specs for clojure.core","archived":false,"fork":false,"pushed_at":"2019-04-07T06:25:13.000Z","size":1295,"stargazers_count":185,"open_issues_count":6,"forks_count":16,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-01T20:30:14.220Z","etag":null,"topics":["clojure","clojure-spec","clojurescript"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/borkdude.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-20T09:25:28.000Z","updated_at":"2024-02-12T09:42:11.000Z","dependencies_parsed_at":"2022-11-01T21:16:10.971Z","dependency_job_id":null,"html_url":"https://github.com/borkdude/speculative","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/borkdude%2Fspeculative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fspeculative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fspeculative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/borkdude%2Fspeculative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/borkdude","download_url":"https://codeload.github.com/borkdude/speculative/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247451666,"owners_count":20940944,"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":["clojure","clojure-spec","clojurescript"],"created_at":"2024-10-03T10:40:55.919Z","updated_at":"2025-10-23T21:27:39.918Z","avatar_url":"https://github.com/borkdude.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"./logo/favicon-160.png\"\u003e\n\n# speculative\n\n[![CircleCI](https://circleci.com/gh/borkdude/speculative/tree/master.svg?style=svg)](https://circleci.com/gh/borkdude/speculative/tree/master)\n[![Clojars Project](https://img.shields.io/clojars/v/speculative.svg)](https://clojars.org/speculative)\n[![cljdoc badge](https://cljdoc.org/badge/speculative/speculative)](https://cljdoc.org/d/speculative/speculative/CURRENT)\n[![Downloads](https://versions.deps.co/borkdude/speculative/downloads.svg)](https://versions.deps.co/borkdude/speculative)\n\nUnofficial community-driven specs for `clojure.core`.\n\n## Quickstart\n\n``` clojure\nClojure 1.10.0\nuser=\u003e (require '[speculative.instrument :as i])\nnil\nuser=\u003e (i/instrument)\n[clojure.core/every-pred clojure.core/max clojure.string/join ...]\nuser=\u003e (subs \"foo\" -1)\nExecution error - invalid arguments to clojure.core/subs at (REPL:1).\n-1 - failed: nat-int? at: [:start] spec: :speculative.specs/nat-int\n```\n\n## Rationale\n\nBy writing, using and maintaining core specs, and reflecting upon them, we get\nthe following benefits:\n\n* Better error messages during development and testing\n* Discover where Clojure and ClojureScript functions behave differently and when\n  possible fix it\n* Discover the established usages of functions within the Clojure community\n* Provide data for [re-find](https://re-find.it), an app that helps you find\n  functions using specs\n\n## Disclaimer\n\nThese specs reflect what we currently know about the newest versions of Clojure\nand ClojureScript. These specs are in no way definitive or authoritative. They\nmay evolve based on new insights and changes in Clojure. These specs have no\nofficial status, are not endorsed by Cognitect and are provided without\nwarranty.\n\n## Installation\n\n### Tools.deps\n\n``` clojure\n{:deps {speculative {:mvn/version \"0.0.3\"}}}\n```\n\n### Leiningen / Boot\n\n``` clojure\n[speculative \"0.0.3\"]\n```\n\n## Usage\n\nSpeculative specs correspond to the namespaces in Clojure:\n\n``` clojure\nspeculative.core   -\u003e clojure.core\nspeculative.set    -\u003e clojure.set\nspeculative.string -\u003e clojure.string\n```\n\nTo load all specs at once, you can require `speculative.instrument` which also\nprovides functions to only instrument speculative specs.\n\n``` clojure\n$ clj\nClojure 1.10.0\nuser=\u003e (require '[speculative.instrument :as i])\nnil\nuser=\u003e (i/instrument)\n[clojure.core/every-pred clojure.core/max clojure.string/join ...]\n\nuser=\u003e (merge-with 1 {:a 2} {:a 3})\nExecution error - invalid arguments to clojure.core/merge-with at (REPL:1).\n1 - failed: ifn? at: [:f] spec: :speculative.specs/ifn\n\nuser=\u003e (i/unstrument)\n...\nuser=\u003e (merge-with 1 {:a 2} {:a 3})\nExecution error (ClassCastException) at user$eval344/invokeStatic (REPL:1).\njava.lang.Long cannot be cast to clojure.lang.IFn\n```\n\n### Usage in testing\n\nTo instrument during testing, you can use the `fixture` from\n`speculative.instrument`:\n\n``` clojure\n(require '[clojure.test :as t])\n(require '[speculative.instrument :as i])\n(t/use-fixtures :once i/fixture)\n```\n\nThis will turn on instrumentation before the tests and turn it off after.\n\nIf you run tests with [kaocha](https://github.com/lambdaisland/kaocha) you can\nuse the [speculative kaocha\nplugin](https://github.com/borkdude/speculative-kaocha-plugin).\n\n## Speculative broke my project\n\nSpeculative specs find, when instrumented, invalid or undefined usage of Clojure\ncore functions. If code is under your control, you can fix it. If the call was\nmade in a library not under your control, you can unstrument the spec using\n`clojure.spec.test.alpha/unstrument`, unload it using `(s/def spec-symbol nil)`\nor disable it within the scope of a body using\n`respeced.test/with-unstrumentation` (see\n[respeced](https://github.com/borkdude/respeced)):\n\n``` clojure\n$ clj\nClojure 1.10.0\nuser=\u003e (require '[respeced.test :refer [with-unstrumentation]])\nnil\nuser=\u003e (require '[speculative.instrument :as i])\nnil\nuser=\u003e (i/instrument)\n[clojure.core/first clojure.core/apply clojure.core/assoc ...]\nuser=\u003e (merge #{1 2 3} 4)\nExecution error - invalid arguments to clojure.core/merge at (REPL:1).\n#{1 3 2} - failed: map? at: [:maps :init-map :clojure.spec.alpha/pred]\n#{1 3 2} - failed: nil? at: [:maps :init-map :clojure.spec.alpha/nil]\nuser=\u003e (respeced.test/with-unstrumentation `merge (merge #{1 2 3} 4))\n#{1 4 3 2}\n```\n\nIf you believe the spec was insuffient, please create an\n[issue](https://github.com/borkdude/speculative/issues).\n\n## Tests\n\nTo execute all tests, simply run `script/test`. Running `script/clean` before\nrunning tests is recommended, especially for ClojureScript on Node. The script\n`script/test` automatically calls `script/clean` for you.\n\nTo specify environments:\n\n    TEST_ENV=clj script/test-runner\n    TEST_ENV=cljs CLJS_ENV=node script/test-runner\n    TEST_ENV=cljs CLJS_ENV=planck script/test-runner\n\n### Number of generative tests\n\nBy default the number of generative tests is set to `50`, but this can be\noverriden by setting the environment variable `NUM_TESTS`:\n\n    NUM_TESTS=1001 script/test\n\n### Run a single test\n\n#### Clojure\n\n    clojure -A:test:clj-test-runner -v speculative.core-test/assoc-in-test\n\n#### ClojureScript (Node)\n\n    clojure -A:test:cljs-test-runner -v speculative.core-test/assoc-in-test\n\n#### Self-hosted ClojureScript (Planck)\n\n    clojure -A:test:cljs-test-runner -x planck -v speculative.core-test/assoc-in-test\n\n### Coal-mine\n\n[Coal-mine](https://github.com/mfikes/coal-mine) is a collection of 4clojure\nsolutions. These can be used to verify speculative specs.\n\nRun a random coal-mine problem:\n\n    script/coal-mine\n\nRun a specific coal-mine problem:\n\n    script/coal-mine --problem 77\n\nRun a range of coal-mine problems:\n\n    script/coal-mine --from 10 --to 15\n\nBoth `from` and `to` are inclusive.\n\nRun with additional checks on `ret` and `fn` specs via\n[orchestra](https://github.com/jeaye/orchestra) (EXPERIMENTAL):\n\n    script/coal-mine --from 10 --to 15 --ret-spec true\n\nTo skip an environment (CLJ or CLJS):\n\n    SKIP_CLJS=true script/coal-mine\n\n### ClojureDocs\n\nTo verify a spec against examples from [ClojureDocs](https://clojuredocs.org),\nrun (for e.g. `update-in`):\n\n    clj -A:test:clojuredocs -v clojure.core/update-in\n\n## Try online\n\n[KLIPSE REPL](https://re-find.it/speculative-repl) with speculative and\n[expound](https://github.com/bhb/expound).\n\n## Origins\n\nThe project started based on two tweets. First @mfikes tweeted\n\n\u003cblockquote class=\"twitter-tweet\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eI still\nhold the view that Clojure’s core fns should have specs. \u003cbr\u003e\u003cbr\u003eEx: While\u003cbr\u003e\n(merge-with + [0 3] {0 7 1 2} {0 3 2 32})\u003cbr\u003eproduces a reasonable result, it is\nnot even a map. A spec would reject 2nd arg.\u003cbr\u003e\u003cbr\u003eWhat if I conclude dot\nproducts are possible via\u003cbr\u003e (merge-with + [0 3] [1 2])\u003cbr\u003e?\u003c/p\u003e\u0026mdash; Mike\nFikes (@mfikes) \u003ca\nhref=\"https://twitter.com/mfikes/status/1053304266239197184?ref_src=twsrc%5Etfw\"\u003eOctober\n19, 2018\u003c/a\u003e\u003c/blockquote\u003e\n\nThen @borkdude tweeted a couple of days later: \u003cblockquote class=\"twitter-tweet\"\ndata-conversation=\"none\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eOr maybe have a\ndevelopment version with guards and a production version without guards (I think\nStu said something like this)\u003c/p\u003e\u0026mdash; (λ. borkdude) (@borkdude) \u003ca\nhref=\"https://twitter.com/borkdude/status/1053404362062606336?ref_src=twsrc%5Etfw\"\u003eOctober\n19, 2018\u003c/a\u003e\u003c/blockquote\u003e\n\n## Issues found\n\n[These issues](doc/issues.md) were found while developing and using speculative.\n\n## Users\n\n[These projects](doc/users.md) are known to use speculative.\n\n## Contributing\n\nIn case this code will ever be useful to `clojure.core`, any contributer to this\nproject needs to sign the [Contributor\nAgreement](https://clojure.org/community/contributing) for Clojure so that any\ncode in speculative can be used in either Clojure or Clojurescript.\n\nPlease have look at the [contributor guidelines](CONTRIBUTING.md) before\nsubmitting a PR.\n\n## Contributors\n\n(Generated by [Hall-Of-Fame](https://github.com/sourcerer-io/hall-of-fame))\n\n[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/0)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/0)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/1)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/1)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/2)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/2)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/3)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/3)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/4)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/4)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/5)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/5)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/6)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/6)[![](https://sourcerer.io/fame/borkdude/borkdude/speculative/images/7)](https://sourcerer.io/fame/borkdude/borkdude/speculative/links/7)\n\n## License\n\nCopyright © 2018 Erik Assum, Michiel Borkent and Mike Fikes\n\nDistributed under the Eclipse Public License either version 1.0 or (at your\noption) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fborkdude%2Fspeculative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fborkdude%2Fspeculative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fborkdude%2Fspeculative/lists"}