Git Product home page Git Product logo

rules's Introduction

rules

clojure/script rule engine

beta quality

Install

;; in deps.edn
{:deps {github-akovantsev/rules
        {:git/url "https://github.com/akovantsev/rules"
         :sha     "87108b4fa2a0caceea1a59c284ac3baf38108819"}}} ;; actual sha

Usage example

https://github.com/akovantsev/rules/blob/main/test/com/akovantsev/rules/test.clj#L56

https://gist.github.com/akovantsev/61476fe57dd8d11c1a492a1a1e82c31a

Rule example

(def autoinc
   (r/rule ::foo                 ;; macro,  rule-id
     [:new id :x x < 5]          ;; find all ids whos :x is < 5
     [:let id :y y]              ;; join on id, where :y is not nil
     [:old id :x ox not= x]      ;; join on id, where prev :x val โ‰  curr :x val

     :foreach                    ;; for each match {:id ... :x ... :y ...}:
     (prn :foreach1 match)       ;; print match map
     (r/change! id :x inc)       ;; ~ (update current-db id :x inc)

     :forall                     ;; once, for matches seq ({:id ... :x ... :y ...} ...):
     (println :once1 matches))) ;; print matches

about

Similar to https://github.com/oakes/odoyle-rules with some differences:

  • odoyle implements rete. rules does not.
  • odoyle discards datoms wich don't have matching tuples in any rules. rules does not, so you can store more things than you query, and don't keep another db map on the side.
  • odoyle allows to compare only(?) value bindings with only their own past value. rules allows you to compare with any past value:
; odoyle:
:what
[foo ::left-of bar {:then not=}]
[bar ::color color]

; rules:
[:new bar ::color color my-compare-with obar]
[:new foo ::left-of bar not= obar]
[:old foo ::left-of obar]

Query / LHS format

Tuples' order within rule does not matter.
Tuples are rearranged during rule parsing.

[tuple-type e a v]
[tuple-type e a v f & args]

tuple-type is one of:

  • :new will use current db for bindings; any change in db's a(ttribute) will trigger rule.
  • :old will use previous db for bindings; does not trigger rule.
  • :let will use current db for bindnigs; does not trigger rule.
; e.g when db changes from {1 {:a 1 :b 1}} to {1 {:a 2 :b 2}}
; change of any :a attribute in db does not trigger this rule,
; because it is not used in :new.
; only changes of :b in db trigger rule:
[:old id :a oa]              ;; :old uses :a, but :a change in db does not trigger rule
[:let id :a a not= (+ 1 oa)] ;; :let uses :a, but :a change in db does not trigger rule
[:new id :b b]               ;; :new uses :b, and any :b change in db triggers the rule

triggers - means rule is queued for query/LHS, and if there are matches - forall/foreach/then/then-finally/RHS/RightHandSide part of the rule is executed:

  • first foreach (if present) is executed with each match from query part (e.g. 10 patches => 10 foreach calls)
  • then forall (if present) is executed once with all the matches (e.g. 100 matches => 1 forall call)

Guards

Guards are (additional to joins) constraints on v bindings (not e ones).
You can reference any other binding from rule in predicate.
By default e binding is passed as first arg to f:

;;[type e a v f & args]:

[:new id :a a < b]  ;; -> (fn [{:as match, :syms [a b]}] (< a b))
[:new id :b b odd?]

But you can specify place with %.

[:new id :a a < b % (+ 10 id %)]  ;; -> (fn [{:as match, :syms [a b id]}] (< b a (+ 10 id a))
[:new id :b b]

Callbacks / RHS

There are 2 rule callbacks available.
You can use both in the same rule.
Both are optional. Rule without any of those means it is just a query.
Order of appearance in rule definition does not matter.

:foreach is called for every match of query/LHS, and sees:

  • current db (same as session in odoyle and clara),
  • all matches seq,
  • current match map,
  • all e and v bindings,
    Forms after :foreach until the :forall or end of rule are the body of callback:
...
[:new id :a a]
:foreach
(println db matches match)
(println id a)
;; => (fn [db matches {:as match, :syms [id a]}]
;;      (println db matches match)
;;      (println id a))

:forall is is called only once, after :foreach (if there is :foreach in the rule), and sees only:

  • current db,
  • all matches seq.
:forall
(println db)
(println matches)
;; => (fn [db matches]
;;      (println db)
;;      (println matches))

rules's People

Contributors

akovantsev avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.