Fork me on GitHub

@ryanbrush: this appears to be invalid is there a way to express this?:


[?it <- accumulator
    [X1 (= ?v x)]
    [X2 (= ?v x)]
    [X3 (= ?v x)]]]


the error we receive

{:type java.lang.IllegalArgumentException
   :message "Only one argument can be passed to a condition."
   :at [clara.rules.dsl$construct_condition invoke "dsl.clj" 53]}


maybe it's worth a PR but i think it'd be helpful to use ex-info here providing the offending form. we had to do a bit of binary chop to find the problem.


@noprompt: Yeah, that error message is pretty bad; we need to share something better there. As for the problem itself, accumulators can only accumulate over simple fact-based expressions rather than everything in a complex expression. So rather than having the [:or ] "inside" the accumulator, you'd need to accumulate over each of X1, X2, and X3 independently.


There might be other ways to break down the problem depending on what you're trying to accomplish. For instance, if X1, X2, and X3 are children of a super type, you can match on the super type. Clara just uses Clojure's type system by default, which also works with Java's type hierarchy and obeys clojure.core/derive if you want to use that to logically derive new types.


here's what we came up with:


[?it <-
  [?x1 <- accumulator :from
   [X1 (= ?v x)]]

  [?x2 <- accumulator :from
   [X2 (= ?v x)]]

  [?x3 <- accumulator :from
   [X3 (= ?v x)]]]]


@ryanbrush: you mentioned derive can you explain how that would work? afaik it only works with namespaced keywords.


Sure. Some users are just using Clojure maps as their fact types and tagging type metadata to them in the form of namespaced keywords, where derive will work.


I think the child in derive can also be an arbitrary record's the parent that needs to be a namespaced keyword. This might be an abuse of the intent of derive, though.


As for the model you came up with: I'm actually not sure that will work...or at least I don't have a test covering binding to the result of an :or expression and that isn't something we've done in production.


right. we're using records and unfortunately that does not work with derive (e.g. (derive Square Shape)).


Another approach would be to break up the accumulators into separate rules, each of which insert some "GroupOfX" type that can be joined to in another rule that combines them all.


oh i see what you're saying wrt derive.


re: your uncertainty. well we'll kick the tires for you and let you know if it does work. 😄


I know the above approach will work.


okay, we'll do that if this turns up dry.


I'm almost certain that you won't be able to bind the result of an ":or", so I think the post above is your best bet. The Rete algorithm splits up the or's into separate rule invocations, and there aren't well-defined semantics for binding the result of other boolean operators in general. But merging the results of separate accumulators will work and is something we do frequently.


By the way, please do report bad or misleading error messages, here or on the github project. I think this is a weak point in Clara right now, especially for new users, so we want to make that better.


@ryanbrush: re: error messages, we're investing in clara so hopefully you can expect more than just an experience report. simple_smile


we focus a lot on producing good error messages at work and use ex-info to not only provide a description but, where possible, provide a prescription to solve the problem.


Sounds great! And I definitely agree with the idea of good, prescriptive error messages. This can be challenging when dealing with the kind of deep macros, like what Clara does when building the Rete network, but is an important step forward.


By the way, I'm planning on moving Clara to its own GitHub project organization and add at least one more committer soon, a colleague who has made some nice pull requests. It shouldn't be too disruptive, but I'll be sure to share here when that happens.