clara

http://www.clara-rules.org/
sparkofreason 2018-01-13T16:02:29.000081Z

I took a shot at forcing conditional inserts in the async handler, if only as an exercise to understand the code better. I got as far as actually getting the insert to occur, basically grabbing the *rule-context* and making it available to the handler, and adding an overload of fire-rules that would accept that as an extra parameter. The rule context was modified so it only contained non-empty :batched-logical-insertions with the facts being inserted as a logical consequence of the rule triggering the async effect. clara.rules.engine/*fire-rules* then also had an extra arity to deal with externally supplied rule context. At this point, I guessed that this approach probably wouldn't work in general, because it looks to me like the :token in the rule context contains the fact bindings which are the logical antecedent. If any of those facts had been retracted in the session between the request and response of the async effect, I assume that clara.rules.memory/add-insertions! would be unhappy, as the token would be inconsistent with the state of working memory. Is that correct?

2018-01-13T16:52:38.000099Z

Yeah. I don’t think that’d be very tractable.

2018-01-13T16:52:57.000053Z

Also working memory is in a mutable state. I think things could easily become inconsistent.

sparkofreason 2018-01-13T18:13:25.000050Z

Makes sense.

sparkofreason 2018-01-13T18:14:58.000023Z

Is there a way to force a rule to fire, even if the fact values don't change? I'm playing around with using an atom to solve this case, but of course the atom reference doesn't change when the atom is reset, only the wrapped value changes, so the rule won't fire after the first time.

alex-dixon 2018-01-13T19:06:06.000040Z

@dave.dixon > Is there a way to force a rule to fire, even if the fact values don’t change? No offense intended at all but that seems off to a bad start even to me…. 😄 What are you trying to do?

2018-01-13T19:20:21.000057Z

Does Clara support namespace aliases in Fact matching? The following throws an exception, but the long-form works:

[encounter-actions/EncounterFindingChanged ..] ;; short -- throws ex
[justice.models.core.actions.encounter.EncounterFindingChanged ..] ;; long

sparkofreason 2018-01-13T19:24:55.000066Z

Yeah, it sounds bad. I'm trying to get the rhs to evaluate when a fact refers to an atom and the atom value changes.

alex-dixon 2018-01-13T19:27:13.000161Z

Interesting. Why an atom?

sparkofreason 2018-01-13T19:28:19.000075Z

I hit a similar issue using a macro in a rule definition.

sparkofreason 2018-01-13T19:29:47.000006Z

So I can put the response from an async request in it.

alex-dixon 2018-01-13T19:33:57.000112Z

Hm ok. Have you tried calling deref on the atom in the condition

sparkofreason 2018-01-13T19:37:00.000088Z

Yes. Doesn't help. In fact, it doesn't work in general for making the bindings, which seems odd.

alex-dixon 2018-01-13T19:39:56.000034Z

Hm. Potentially wild things I’d try: 1. (deref a) instead of the macro if you’re not already, 2. :test expression

sparkofreason 2018-01-13T19:41:35.000070Z

Have tried both. Deref doesn't help, but the reader macro works in a test and rhs.

2018-01-13T19:41:58.000062Z

@dadair it should. If it doesn’t it probably needs to be looked at.

2018-01-13T19:42:03.000020Z

As an issue I mean

alex-dixon 2018-01-13T19:53:21.000060Z

Well I’m confused. @mikerod might be able to shine light on it

2018-01-13T20:31:41.000061Z

@mikerod I'll file an issue, I replicated it with the intro to clara example just moving the defrecords into another ns

2018-01-13T20:32:27.000007Z

@dadair if it’s cljs perhaps it relates to some recent issues already out there. I thought it was fixed though.

2018-01-13T20:32:34.000136Z

nope just .clj

2018-01-13T20:32:39.000026Z

on 0.17.0

2018-01-13T20:32:47.000068Z

Hmm. I’m traveling today so can’t look.

2018-01-13T20:33:29.000035Z

I am not sure I understand still. Been traveling today though so somewhat on and off here

2018-01-13T20:34:02.000119Z

It should definitely support aliasing.

2018-01-13T20:34:44.000016Z

Unless you didn’t include the alias in the ns or something. Hah. If you could make like a small example where it isn’t working that’d be nice to see.

2018-01-13T20:36:41.000045Z

(ns abc.other)

(defrecord SupportRequest [client level])

(defrecord ClientRepresentative [name client])

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(ns abc.core
  (:require [clara.rules :refer :all]
            [abc.other :as other]))

(defrule is-important
  "Find important support requests."
  [abc.other.SupportRequest (= :high level)] ;; full path here works; change to other/SupportRequest and an exception is thrown
  =>
  (println "High support requested!"))

(defrule notify-client-rep
  "Find the client representative and request support."
  [abc.other.SupportRequest (= ?client client)]
  [abc.other.ClientRepresentative (= ?client client) (= ?name name)]
  =>
  (println "Notify" ?name "that"
          ?client "has a new support request!"))

(-> (mk-session 'abc.core)
    (insert (other/->ClientRepresentative "Alice" "Acme")
            (other/->SupportRequest "Acme" :high))
    (fire-rules))

2018-01-13T20:37:29.000051Z

the above example reproduces the problem

2018-01-13T20:38:56.000055Z

Record types in clojure are referred to by their Java Class name

2018-01-13T20:39:02.000044Z

You have to use import for those.

2018-01-13T20:39:17.000106Z

ah right

2018-01-13T20:39:22.000128Z

They don’t support ns aliasing. Their builder functions do but not the type class itsel

2018-01-13T20:39:32.000125Z

Sort of a clj quirk that is unfortunate.

2018-01-13T20:39:52.000054Z

Clara is just using clj for the symbol resolution there though.

2018-01-13T20:41:06.000154Z

I think I’d have to see an example here. Hah

2018-01-13T20:42:33.000071Z

I always forget about imports, thanks!

👍 1
sparkofreason 2018-01-13T21:17:22.000019Z

I'll post it when I get home.

sparkofreason 2018-01-13T22:46:49.000038Z

Playing around with atoms in facts. Both facts above are maps. The ::specs/Tag contains one attribute :tags which is an atom. The ::specs/Time fact is there just to force the rule to fire when the value wrapped by the :tags atom is changed. This is the version of the rule that works.

sparkofreason 2018-01-13T22:49:37.000038Z

Apply deref in the fact binding does not work, which is surprising. Same effect whether deref or @ is used. When written this way, the rule just never fires.

sparkofreason 2018-01-13T22:54:09.000111Z

This was the original attempt, which doesn't work either, and led to me adding the ::specs/Time fact. Of course perhaps this doesn't work because there's something fishy about deref in the LHS, as seen in the previous example. I originally thought it was because it was assumed that if a binding value didn't change, than neither would a function of that bound value. But there seems to be more going on when deref is involved.

alex-dixon 2018-01-13T23:22:28.000030Z

@dave.dixon so in the one that works are there different time facts with different time values?

sparkofreason 2018-01-13T23:24:43.000068Z

@alex-dixon I haven't tried multiple time facts. I think it should work correctly with upsert, though. Retracting existing fact would lead to retraction of the generated Tag facts, and the subsequent insert would generate an updated set.

sparkofreason 2018-01-13T23:27:48.000024Z

I was hoping to get away from having auxiliary unconditional facts like Time. The other version of async is structurally similar, but there the external fact is Response, which is correlated via a rule with the original Request. There's an additional rule that forces retract of Response when the corresponding Request no longer exists.

sparkofreason 2018-01-13T23:29:57.000035Z

Given that both approaches require unconditional inserts, the Request/`Response` pattern is arguably cleaner, as the atom approach requires some extra coordination outside of the rulebase to correctly update the session when the atom value changes.

👍 1
alex-dixon 2018-01-13T23:37:11.000040Z

I’ll definitely keep this in mind as I try to do similar things. Wish I could be more help but it gets hard for me to reason about and I find my thinking tending toward more immutable approaches which seems like the opposite of the overall approach (fewer intermediate facts/garbage). I’ll try to experiment next time I’m at a laptop

alex-dixon 2018-01-13T23:38:48.000063Z

Curious, did you try a callback in the rhs and logical insert? Seem to recall that being your first approach. If so what happened? Loss of session context?