Fork me on GitHub
#clara
<
2015-11-30
>
devn17:11:13

@ryanbrush: Is it possible to use [:or [?foo <- my/accumulator [X (= a 1)]] [?bar <- my/accumulator [Y (= b 2)]]]

devn18:11:15

@ryanbrush: here's a sketch of what we imagine:

(defrule thing-a
  [?foo <- [:or
            [my/accumulator [Foo (= a 1)]]
            [:not [Foo (= a 1)]]]]
  =>
  (when-not (nil? ?foo)
    (insert! (map->Bar ?foo))))

devn18:11:50

We want Foo to be optional, but when it exists, we'd like to accumulate over matching Foo facts.

ryanbrush18:11:56

The best way to solve this problem is to have my/accumulator provide a default value. That way if there are no Foo records, the default value will be returned from the accumulator and propagate down.

ryanbrush18:11:25

For instance, the provided sum accumulator will return a sum of zero if there are no items to sum.

devn18:11:57

Funnily enough, we talked about that a bit.

ryanbrush18:11:08

As a side note, you can't quite think of [:or]'s in rules quite like you'd think of or expressions in Clojure. [:or ..] doesn't "return" a value that can be bound to. (In fact, in the Rete algorithm [:or] actually causes two separate rules to be generated for each branch.)

ryanbrush18:11:38

@devn By the way, when fiddling with this I noticed Clara isn't reporting an error if a user attempts to bind a variable in an unsupported location. This is a bug, so I logged it here https://github.com/rbrush/clara-rules/issues/144

devn19:11:23

@ryanbrush: that is good to know, indeed

devn19:11:19

@ryanbrush: trying to be respectful of your time here, but another question: with accumulators, can I make a function which produces an accumulator and use that in the LHS?

devn19:11:19

like

[Bar (= ?x x)]
[?foo <- (make-accumulator ?x) :from [Foo (= ?y y]]

devn19:11:50

this basically comes down to wanting to tweak the initial value to the accumulator

ryanbrush19:11:28

@devn Definitely! The accumulators in clara.rules.accumulators are all just functions that return accumulators and should be a good reference. They use constants for the initial value but you can easily pass it as a param.

devn19:11:30

asking because all of the examples i've seen are def'd and only took the field to compare on

devn19:11:05

cool, guess I could have tried it, but didn't know calling a function in place of a def'd accumulator had any edges i needed to be aware of

ryanbrush19:11:38

Any function that returns an accumulator will work. The provided ones just happen to use fields.

devn19:11:12

@ryanbrush: a couple other small bits - Any reason that things like min/max couldn't optionally take a function to coerce the value of a field to be comparable? For instance, dates. - Using reduce-to-accum seems to imply :supports-retract given that min, max etc. call comparison-based, which in turn calls reduce-to-accum with an initial value of nil. Is that true? I remember reading the pull request a couple of times, but are there any good rules of thumb to use when building accumulators to know that you've supported retraction? Now that I write that out, my other question is: When retract is mentioned, is it talking about automatic retraction, explicit retraction, or both?

ryanbrush19:11:01

As long as your accumulator has a retract-fn, it supports retraction. reduce-to-accum is a convenience function that makes it easier to turn general reduce functions into accumulators by (by default) creating a retract-fn that removes the retracted item from the accumulator and re-runs the reduce. In some cases you can create more efficient retract-fn implementations. See, for instance, sum...to retract a value from a sum you can just subtract the value, which is of course more efficient than re-running the entire sum with the value removed.

ryanbrush19:11:17

This applies to both automatic and explicit retraction.

ryanbrush19:11:42

Really the retract-fn is just to "undo" the presence of an item in the accumulated value, like subtraction undoes the value in a sum. the reduce-to-accum just offers a "brute force" way to do this for all reduce functions by re-running them. In some very performance-sensitive cases where you don't care about retraction you can skip supporting it, but I wouldn't do so unless you have a special need.

ryanbrush19:11:59

FWIW, Clara accumulators work pretty much exactly like the same concept in Jess and Drools. You just write the functions in Clojure. 😉

ryanbrush20:11:16

(Also, I need to drop offline for a bit but will be back on later today.)

devn20:11:45

@ryanbrush: that's a lovely explanation, thank you!

devn20:11:47

(mentioned the @logbot so there's actually a log of some of this stuff, it scans every 2 hours, so it should join shortly.)