Fork me on GitHub
dominicm07:05:58 Is it possible to set a fact type in such a way that this isn't needed?


@dominicm Could you give a minimal failing example? That doesn’t sound like something that should happen but we might be thinking about different things.


That might not be my problem actually. But when my type-fn is :a I get an error about alpha nodes if I try and refer to e.


@dominicm I don’t see any context in your question. Did you post an example? You said where “this isn’t needed”, what were you referring to?


FYI I posted an example snippet overriding the fact type for something like tuples above. Well I put them in maps for key access by name.


You can’t refer to bare symbols that “represent” lookup keys in a rule condition if you are asking that though with regard to “referring to e. Clara currently only offers special syntactic access directly to record or “Java bean” field/accessors. If you are using generic types it doesn’t attempt to resolve like the symbol e to (:e this) This may be what you’re asking


It’d actually be interesting to explore making Clara offer that sort of syntax though on more generic data.


Sorry. I got distracted when I got in and forgot to post it. On phone again, once I'm at the keyboard I will do it.


Hi, can someone point me to some literature on how to write my own rules engine? I have trouble finding usefull stuff.


clara is heavily based on this paper if you really want to write your own "engine"

👍 4

I am just curious, thank you @zylox


Caveat: that paper is really old. There are many modern day rete extensions that Clara uses too. The paper is really good at explaining the foundations of rete though.


also just about any info you can get on rete will be intersting


The Drools/Jboss people also had some good blogs out there regarding rete.


Then there is also this great blog about Clara/rete here (I wrote it 😛 )


@mikerod thank to you too


(ns roll-alt
    [clara.rules :refer :all]
    [ :as fg]
    [clara.rules.accumulators :as accum]
    [rhizome.viz :as rhizome]))

(defrecord D [e a v])

(defrule todos-done-invisible
  ;; [:todo/title (= ?e e)]
  [:todo/done (= (:v this) false) (= ?e (:e this))]
    (->D ?e :todo/visible true)))

  (-> (mk-session
        :fact-type-fn :a
        :ancestors-fn (constantly [:all]))
        (->D "foo" :todo/title "FOO!")
        (->D "foo" :todo/title "FOOBAR!")
        (->D "foo" :todo/done true)
        (->D "blah" :todo/done false))
      (query visible-datoms)))
@mikerod the first line in the defrule doesn't work.


@dominicm that seems related to the explanation I at least attempted to give above about when you can refer to “field name” symbols directly in a rule


Clara compiler cannot statically/compile time know what those symbols mean since the rule condition has a custom type that can’t be introspected


It’d be interesting if Clara added support for you to explicitly state “fields” that go with custom fact types. But nothing like that right now. You’d have to do (:e this) instead of e or use destructuring as you have in the past


When rules have records or other types of classes directly in the rule condition Clara compiler will reflect on the class to determine its available field names. When it can do that, you can refer to field names directly as their name symbol.


> Clara compiler cannot statically/compile time know what those symbols mean since the rule condition has a custom type that can’t be introspected That explains it to me, that's entirely what I was missing. It obviously worked when I wrote the form this way:

(defrule todos-done-invisible
  [?d <- D (= a :todo/title)]
    (->D (:e ?d) :todo/visible true)))
Which has a trade-off.


I was a bit confused because it was still a record, ergo it should work (in my head).


I looked for something to provide a custom keys function, and when I didn't find it, I presumed I was missing something.


@dominicm It’d be interesting to have a fn that takes your custom “type” and returns a set of “field keys”


and that’d be caleld when the compiler was compiling rules and then it’d allow for the shorter accessors


Yeah, Clara just has built-in special casing to class types since they have reflection utilities available to find out how to resolve those symbols


I’ll add that you probably don’t want to write all your rules against a single type like D, so that isn’t a pattern I’d advocate

thisisfine 4

It’d lead to a lot of thrash in the rules since every insert/retract would potentially affect every rule


The fact type serves as, what I’d call, the first level of discrimination/partitioning in the network.


My data set isn't particularly large I think. I'm struggling to come up with good fact types for my domain. But that's likely a thing on my side.


The problem I have is that I'm operating in a large domain, and I'm asserting facts on a per-key level, instead of whole entities.


I suppose I could generate something like S3BucketFact which takes an id, key and value. But it's not so different from my datom record.


Haven’t read through all this, but FYI the ancestors-fn sounds like it could help you as well. So you could have rules matching all S3BucketFact as well as more rules matching only “subtypes” of S3BucketFact


Yeah, I'm deriving all my records, so that I can do a query for all "Props"


I've introduced a meta "Resource" which goes into the "e" slot too, to make it easier to query for resources without getting the duplicates from props.


Or should I be creating records at the level of S3BucketName, s3bucketYYY, etc.?


don’t know about that


maybe the “fact type” should be the “attribute” slot of these sorts of facts? I’m not sure


I guess I can invent static generation of that somehow later. It feels less "open".


@dominicm I think it’d come down to the sort of rules you were needing to write


the fact type is just a useful way to group facts across the network that are coming from an external and also to categorize derived data


I don’t see the fact types as being a closed off concept. They are just what the rules refer to to match things, join things, and check other constraints. All of this is probably too vague though. I don’t have a good enough understand of what your data might be looking like and the sorts of things you want to do with it.


This now does make me wonder what sort of “fact type”s are used in #precept


I've been thinking the same, and also how certain problems of querying are solved, particularly around duplicate entities, potentially. There must be a few additional aspects to the querying in precept over what Clara provides alone.


It was interesting to see several rules disappear when I got rid of the D record in favour of XFact records. I'm interested to see what happens with X-Y records.


Without having at least the concept of "S3Bucket" and "S3BucketFact" it's quite difficult to do "All S3 buckets must have :foo 2", because the ?e is duplicated across so many facts. I could perhaps use an accumulator, but it seems more natural to have strict types.


I've written a macro which is run like (defType Box) will generate: - (defrecord Box) - (defrecord BoxFact) - (derive Box ::Type) (derive BoxFact ::Fact) - Function for converting to Box from other types - Function for "scoping" a series of [a v] into Facts. I'm moving rather fast. I haven't gone really deep yet, but I'm going to see how far I can get without declaring every possible attribute on a Box as a type.


It's certainly cool to see the addition of a route to an app cause the subnet to change, and the route53 records & load balancers to be created.

👍 4

Sorry, I'm tripping over things I don't understand again,

(defquery created-by
  [?fact <- ::Fact
   (= ?by (:e this))
   (= (:a this) :roll/created-for)
   (= (:v this) ?for)])
I'm getting back a result where :?for is different to what I input as a parameter: (query s created-by :?for (->Box "app" nil)). I don't really understand why, any advice?


the query returns all 3 bound variables and 1 match as expected


if I tweak the query or the inserted ::Fact, the results go away as expected too