{"id":32181938,"url":"https://github.com/alexei-matveev/regelwerk","last_synced_at":"2025-10-21T22:55:44.827Z","repository":{"id":62432624,"uuid":"291140434","full_name":"alexei-matveev/regelwerk","owner":"alexei-matveev","description":"Making facts of other facts ...","archived":false,"fork":false,"pushed_at":"2024-12-18T20:52:20.000Z","size":179,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-21T22:55:44.473Z","etag":null,"topics":["clojure","datalog","rule-engine"],"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/alexei-matveev.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-08-28T20:30:33.000Z","updated_at":"2024-12-18T20:52:24.000Z","dependencies_parsed_at":"2024-12-18T21:29:16.874Z","dependency_job_id":"7bd8bf9c-635e-4860-91ee-871d920b6e92","html_url":"https://github.com/alexei-matveev/regelwerk","commit_stats":{"total_commits":226,"total_committers":1,"mean_commits":226.0,"dds":0.0,"last_synced_commit":"0e8d4d725f8f0d090a8c7c18de49fd66efb82189"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alexei-matveev/regelwerk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexei-matveev%2Fregelwerk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexei-matveev%2Fregelwerk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexei-matveev%2Fregelwerk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexei-matveev%2Fregelwerk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexei-matveev","download_url":"https://codeload.github.com/alexei-matveev/regelwerk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexei-matveev%2Fregelwerk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280348058,"owners_count":26315367,"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-10-21T02:00:06.614Z","response_time":58,"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":["clojure","datalog","rule-engine"],"created_at":"2025-10-21T22:55:42.729Z","updated_at":"2025-10-21T22:55:44.821Z","avatar_url":"https://github.com/alexei-matveev.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rules:: Facts -\u003e Facts'\n\n[![Clojars Project](https://img.shields.io/clojars/v/f0bec0d/regelwerk.svg)](https://clojars.org/f0bec0d/regelwerk)\n\nTo keep thing simple and see where it leads us we postulate here that\na rule or a rule set is just a function of facts producing new\nfacts. No more, no less.\n\nAnother   \"weaker\"    constraint   of   the   design    was   to   use\nDatalog/Datascript Query  to produce  a \"Result  Set\" from  the \"Input\nFacts\" and to  generate the \"Output Facts\" from the  \"Result Set\" in a\nsufficiently  flexible  way.   The ``mapcat``-  aka  ``flatMap``-style\nconcatenation of the \"Output  Facts\" independently generated from each\nrow of the \"Result Set\" appeared then to be a natural choice.\n\n## Example Usage\n\nNote that  it does  not always  make sense  to mix/conflate  the input\nfacts and the  output facts, here illustrated by use  of two different\nhuman languages:\n\n```clojure\n(let [facts [[1 :is \"odd\"]\n             [2 :is \"even\"]]\n      rules (defrules\n              {:find [?number]\n               ;; But only even numbers from the set of English\n               ;; facts by Datalog query:\n               :when [[?number :is \"even\"]]\n               ;; For each number produce this set of facts in\n               ;; German:\n               :then [[?number :ist \"gerade\"]\n                      [(inc ?number) :ist \"ungerade\"]\n                      [(dec ?number) :ist \"ungerade\"]]})]\n  (rules facts))\n;; =\u003e\n#{[1 :ist \"ungerade\"]\n  [2 :ist \"gerade\"]\n  [3 :ist \"ungerade\"]})))\n```\n\nStill many rule  engines insist on \"inserting\" deduced  facts into the\nvery database that was \"queried\".\n\n## Next Level: Lamda Calculus with Sets of Facts\n\nWould such a calculus be useful or is it just mind gymnastics just to\n[reinvent](https://en.wikipedia.org/wiki/Language_Integrated_Query)\n[the](https://www.w3.org/TR/rdf-mt/#rules)\n[wheel](https://en.wikipedia.org/wiki/Relational_algebra)? Here is an\nexample of Binary Rule that compares two datasets, `en` and `de`, to\ndeduce a translation between English and German:\n\n```clojure\n(let [en [[1 :is \"odd\"]\n          [2 :is \"even\"]]\n      de [[1 :ist \"ungerade\"]\n          [2 :ist \"gerade\"]]\n      tr (defrules\n           {:from [$en $de]\n            :find [?word ?wort]\n            :when [[$en ?n :is ?word]\n                   [$de ?n :ist ?wort]]\n            :then [[?word :eqv ?wort]\n                   [?wort :eqv ?word]]})]\n  (tr en de))\n;; =\u003e\n#{[\"odd\" :eqv \"ungerade\"]\n  [\"gerade\" :eqv \"even\"]\n  [\"ungerade\" :eqv \"odd\"]\n  [\"even\" :eqv \"gerade\"]}\n```\n\nThink of composable programs with \"Rules\" operating on \"Sets of\nFacts\".  Think of N-ary Rules taking zero, one or more \"Sets of Facts\"\nas arguments and producing even more of that as a result:\n\n    Rules:: Facts1 -\u003e Facts2 -\u003e ... -\u003e FactsN\n\nThink of  `select ... from  facts1 join facts2  ...`.  Or is  a `join`\nrather  bad analogy  because  it  is too  \"cartesian\"?   What kind  of\nexpressions besides  function application should be  allowed? In other\nwords,  what are  the built-ins?  Some set  operations like  union and\ndiff?   Joins?   A  plain  irreversible  union  that  trows  facts  of\ndifferent domains  in a  single pot/set  is probably  not a  good idea\ntype-wise.\n\n## Build \u0026 Deploy to Clojars\n\nFirst you may consider bumping the version in\n``project.clj``. Remember that you cannot overwrite non-SNAPSHOT\nversions and what gets published, stays public. You habe been warned!\nWith that said, here is the memo:\n\n    $ lein clean\n    $ lein jar\n    $ lein test\n    $ lein deploy clojars\n\nThe artifact goes to\n[f0bec0d/regelwerk](https://clojars.org/f0bec0d/regelwerk) repository\nso the user name will be ``f0bec0d``.  The passwort is one of the\nDeploy Tokens generated at Clojars, likely stored in your KeePass.\n\n## Links\n\n* Maybe one shlould read [PAIP](https://github.com/norvig/paip-lisp)\n  before even attempting ...\n* The whole RDF Subject and\n  [inference](https://jena.apache.org/documentation/inference/) is\n  huge to swallow.\n* See [Datascript](https://github.com/tonsky/datascript) dialect of\n  Datalog.\n* [Minikusari](https://github.com/frankiesardo/minikusari) in less two\n  dosen lines of code generates transaction datoms (facts) from the\n  result set of a Datascript query.\n* [naga](https://github.com/quoll/naga) seems to be generating facts\n  from rules.\n* [Souffle](https://souffle-lang.github.io/simple) Lang uses plain\n  *.facts for input- and output relations.\n* [Formulog](https://github.com/HarvardPL/formulog) delivers an\n  executable JAR that transforms program \"text\" to fact database\n  \"text\" bei executing Rules\n* [Crepe](https://crates.io/crates/crepe) DSL in Rust that\n  Acknowledges Souffle \u0026 Datalog for inspiration.\n* Functional Production\n  [Rules](https://leanpub.com/readevalprintlove004/read#leanpub-auto-a-simple-implementation-of-a-purely-functional-production-rules-system)\n* Another unifier-based query\n  [engine](https://github.com/jimmyhmiller/one-hundred-lines-or-less/tree/master/libraries/clojure/query-engine/src/query_engine)\n* [Meander](https://github.com/noprompt/meander)\n* [Knowledge Machine](https://www.cs.utexas.edu/%7Emfkb/km.html) from\n  around 1997 or before.\n\nMaybe an interesting read:\n\n* Better Together: Unifying Datalog and Equality Saturation [PDF](https://arxiv.org/pdf/2304.04332.pdf)\n* [Is Datalog a good language for\n  authorization?](https://neilmadden.blog/2022/02/19/is-datalog-a-good-language-for-authorization/)\n  and the HN\n  [discussion](https://news.ycombinator.com/item?id=30400886)\n* [Fixpoints for the Masses:\n  Programming with First-Class Datalog Constraints](https://dl.acm.org/doi/pdf/10.1145/3428193) proposes composable Datalog programs as Data.\n* [AP5](https://www.ap5.com) dates back to at least as early as 1986.\n  \n\n## ChangeLog\n\nVersion 0.0.3-SNAPSHOT:\n\n* Rules involving more than one dataset: `{:from [$a $b], :when [[$a\n  ...], [$b ...]], :then [...]}`.\n* BREAKING: no more list-syntax for rules. Convert the lists to maps\n  with `:find`, `:then`, and `:when` keys in that sequence.\n* BREAKING: Singular form `(defrule vars expr where)` is no more\n  supported. Use maps instead.\n* BREAKING: `(comment ...)` forms in Rules are no more handled\n  specially. Use Reader Comment `#_` instead.\n* DEPRECATED: A rule that returns any facts derived from an empty\n  dataset likely violates \"empty graph lemma\". Therefore the special\n  case of `{:then [...]}` akin to `(constantly [...])` that returns\n  facts \"no matter what\" must go. In the unlikely case that this edge\n  case affects you, consider some seed- or ground facts in your\n  dataset like `[\"version\" :ge \"0.0.3\"]` or `[\"water\" :is \"wet\"]`...\n\n## License\n\nCopyright © 2022 Alexei Matveev \u003calexei.matveev@gmail.com\u003e\n\nDistributed under the Eclipse Public License either version 1.0 or (at\nyour option) any later version.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexei-matveev%2Fregelwerk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexei-matveev%2Fregelwerk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexei-matveev%2Fregelwerk/lists"}