Fork me on GitHub

Well @jfntn , I used to classify I kind of link status to avoid chains of ifs, but my experience is that it was more complex than I thougth, so I changed to datascript to be more clear and simple


here is an example what I did

(defn limite [chave]
  (read-string (chave (:referencia (:config sistema)))))

(defrecord IcmpLocalizacao [nome-link localidade jitter latencia perda])
(defrecord Problema [mensagem nome-link localidade])

(defrule ligacao-fragmentada
         [?local <- IcmpLocalizacao (= ?nome-link nome-link) (= ?localidade localidade) (> perda (limite :perda))]
         (insert! (->Problema :fragmentada ?nome-link ?localidade)))

(defrule telefonia-degradada
         [?local <- IcmpLocalizacao (= ?nome-link nome-link) (= ?localidade localidade) (> jitter (limite :jitter)) (> latencia (limite :latencia)) (<= perda (limite :perda))]
         (insert! (->Problema :degradada ?nome-link ?localidade)))

(defrule perda-qualidade
         [?local <- IcmpLocalizacao (= ?nome-link nome-link) (= ?localidade localidade) (> jitter (limite :jitter)) (<= latencia (limite :latencia)) (<= perda (limite :perda))]
         (insert! (->Problema :qualidade ?nome-link ?localidade)))

(defrule atraso-telefonia
         [?local <- IcmpLocalizacao (= ?nome-link nome-link) (= ?localidade localidade) (<= jitter (limite :jitter)) (> latencia (limite :latencia)) (<= perda (limite :perda))]
         (insert! (->Problema :atraso ?nome-link ?localidade)))

(defrule sem-comunicacao
         [?local <- IcmpLocalizacao (= ?nome-link nome-link) (= ?localidade localidade) (= jitter -1) (= latencia -1) (= perda -1)]
         (insert! (->Problema :comunicacao_locais ?nome-link ?localidade)))

(defquery problemas-comunicacao []
          [Problema (= ?mensagem mensagem) (= ?nome-link nome-link) (= ?localidade localidade)])

(defn gerar-fatos [fatos]
  (let [regras (atom #{})]
    (doseq [fato fatos]
    (let [link (first (first fato))
          valor (second (first fato))
          localidade (:localidade valor)
          icmp (:icmp valor)
          jitter (:jitter icmp)
          perda (:perda icmp)
          latencia (:latencia icmp)]
    (swap! regras conj (->IcmpLocalizacao link localidade jitter latencia perda))))

(defn compilar-regras [fatos]
  (into #{}
        (-> (mk-session 'correlacao.item.regraslink)
            (insert-all (gerar-fatos fatos))
            (query problemas-comunicacao))))


in datascript

(defn- classificar-elemento [conn tipo op-jitter op-perda op-latencia jitter perda latencia]
  (let [resultado (d/q '[:find ?localidade ?link
                         :in $ ?op-jitter ?op-perda ?op-latencia ?ref-jitter ?ref-perda ?ref-latencia
                         [?e :link ?link]
                         [?e :localidade ?localidade]
                         [?e :jitter ?jitter]
                         [?e :perda ?perda]
                         [?e :latencia ?latencia]
                         [(?op-jitter ?jitter ?ref-jitter)]
                         [(?op-perda ?perda ?ref-perda)]
                         [(?op-latencia ?latencia ?ref-latencia)]
                       conn op-jitter op-perda op-latencia jitter perda latencia)]
    (if (= #{} resultado)
      (resultado->set resultado tipo)


(classificar-elemento @conn :fragmentada nevermind > nevermind jitter perda latencia)


I see that datascript was more flexible, but you can use any other ways to define rules for your job, like in or even clojure.logic


@jfntn I guess I don’t fully grasp your problem statement


However, Clara has certainly been used in a way that creates logical “groups” and/or “hierarchies” of rules


You can have intermediary facts inserted that act as “blockers” for some rules, or have some rules not be able to be satisfied until certain facts are present


However, this isn’t done by “turning off” rules or by excluding rules from the rulebase/session created, instead it is done purely through logical relationships the rules express


Also, the “type” dispatch function used by Clara rules LHS conditions is pluggable. So you could do things with ad hoc hierarchies of Clojure


has some details on the custom “fact type” fn (and similarly the associated “ancestors” fn


I may be speaking way too vague to help answer your question though


Ok interesting, I guess I was wondering if it was valid to consider an approach where the app dynamically loads/unoads a different session depending on some external state machine


I think @fabrao may have had some interesting points above too. There is a chance that forward-chaining rules aren’t the best approach to certain sorts of problems


I would say it is not a good situation to have when your rules end up being of the form: A -> B; B -> C; C -> D; D -> E - when that chain starts to get really long and only one linear flow of logic


@jfntn well consider one of the compelling strengths of a Rete-based forward chaining rules impl like Clara - it stores a bunch of state to avoid re-calculation of join and non-joining constraints done in previous states of the system


if you flip from session to session, they will not share any of this state with one another. there is a chance that that is suitable for your needs. I’m just calling it out.


Also, the time to create a session is typically non-trivial if you have a fair number of rules involved. So often the pattern is to keep the sessions in memory. There will be limitations to how many rule sessions you can store in memory at a time. The size of them is mostly dependent on the number or rules and conditions involved


So I guess I was assuming you could somehow keep the facts but change the rules, not sure if that makes sense in practice


@jfntn you could query the facts produced from a fire-rules execution of a session


This article includes this definition for an fsm:


and you could insert those facts into any other sessoin that uses them


(def fsm {'Start          {:init             'Ready}
          'Ready          {:missing-password 'Password-Error
                           :missing-email    'Email-Error
                           :submit           'Submitting}
          'Password-Error {:change-password  'Ready}
          'Email-Error    {:change-email     'Ready}
          'Submitting     {:receive-success  'Success}})


I believe I just read this a few days ago


I hadn’t really thought much about it though.


I was imagining something like rules as transitions instead of keywords, and a sort of recursive definition where the Symbols on the right-hand side could be whole FSMs


Have you found that it is difficult to express the FSM sort of logic just using 1 session?


Wouldn’t you have to encode the transitions across many rules though? Maybe I just want something that will define a valid session from recursing over this tree


It isn’t immediately clear to me how it would look


Perhaps this article is enough to go off to make a toy example in Clara with 1 session


But it may not be sophisticated enough to show anything useful - I don’t know


I’m just trying to find the simplest solution the the problem and I’d hope there would be a nice mapping of the idea to a single session. Just to avoid the details of bouncing around between them


You could certainly have a map of “session name key” -> session


and have RHS’s produce a fact that says what is the “next session” to evaluate


Or to flip that and just have the RHS produce a fact that your outer FSM uses to transition to the next step - I think that is what you said above


it seems like workflow engine


JBoss had jBPM I think


which basically (I believe) is a FSM sort of system that triggers Drools rules at certain stages


(among other things)


So I guess the idea came up there as well. I’m still just curious about whether or not a single session can’t express an FSM in the sense of the web app article though


Seems like it should be possible, but the article is only trying to represent a single FSM, and to extend that definition to a Statechart you have to imagine that each state on the right is not final but can have recursive fsm definitions.


You’d be able to chart the whole app with something along those lines:


(def app-statechart
     {'Start          {:init 'Ready}
      'Ready          {:missing-password 'Password-Error
                       :missing-email    'Email-Error
                       :submit           'Submitting}
      'Password-Error {:change-password 'Ready}
      'Email-Error    {:change-email 'Ready}
      'Submitting     {:receive-success 'Success}}}
    :signup {... }}
   'User  {...}
   'Admin {...}})


The key distinction is that for a given node in the tree, valid transitions at that state are the union of its and its parents’ transitions (or rules in our case), so rules that are not in this set are illegal




Dont you think clara is too complex for that?


you can define many ruleset and apply it in your context, so you can dynamic it for your needs


There is no engine there, but I’m assuming you put it on top of something like datascript


and I think you end up in a backwards-chaining situation there


which may be find and fit the need. I think it just comes down to what you want out of it.


also, you have to consider any performance concerns you may have, clj/cljs support (I think datalog-rules would be fine in either), etc


No I think conceptually we do want forward chaining and all the caching goodness from clara, just trying to wrap my head around what type of application-level integration we could have