clara

http://www.clara-rules.org/
ethanc 2018-10-25T13:25:46.000100Z

Probably not relevant and more of an FYI, but if the condition? is simply equality it could be done with a third argument to =.

(r/defrule some-rule
  [Request (= ?resource resource "GET")]
  =>
  (println ?resource))

eraserhd 2018-10-25T13:33:46.000100Z

@souenzzo wait, really? Is there a list of functions that Clara handles specially?

eraserhd 2018-10-25T13:34:02.000200Z

I mean, knows how to join, other than =?

souenzzo 2018-10-25T13:42:59.000100Z

[Request (= resource "GET")]
=>
(prn "GET")
Onde it will only match if resource is GET 😅

souenzzo 2018-10-25T13:43:51.000100Z

you can also do

[?request <- Request (= resource "GET")]
=>
(prn ?request)
Will print the "full record"

souenzzo 2018-10-25T13:46:15.000100Z

I use = and contanis?. I learned it when I did not understand how clara works then never search for it.

2018-10-25T13:57:00.000100Z

As a newcomer it’s not super clear which functions are built-in. This is definitely an advanced user library :)

2018-10-25T13:59:30.000100Z

Thanks @ethanc ! This is actually something that I did need (for a different rule)

ethanc 2018-10-25T14:22:04.000100Z

I'm believe that the only functions that Clara handles specially are the equality expressions: https://github.com/cerner/clara-rules/blob/master/src/main/clojure/clara/rules/compiler.clj#L213-L223

3👍
2018-10-25T14:36:54.000100Z

Yeah, = is the only special thing

1👍
2018-10-25T14:37:17.000100Z

It still means = too, but it can also be used for unification purposes/binding variables

2018-10-25T14:37:47.000100Z

nothing is special about contains? (or anything else)

2018-10-25T14:54:16.000100Z

Agreed with other comments that it is just = that is special when used as a top-level value in the constraint to create/join on bindings (starting with ?)

2018-10-25T14:54:40.000100Z

that has perf benefits in that those joins are done by hashing

2018-10-25T14:55:12.000100Z

otherwise you can use arbitrary functions and they should behave the same as elsewhere

2018-10-25T14:55:46.000100Z

While reading the docs, it goes from straightforward to advanced very quickly. In http://www.clara-rules.org/docs/rules/, I fully understand the free-lunch-with-gizmo because there's no ? binding, you're matching that an item is :gizmo, but then in the sophisticated example:

(defrule grant-discount-months  
  [Purchase (= ?month (get-month date))]
  [DiscountMonth (= ?month month)]
  =>
  (insert! (->GrantDiscount :for-month ?month)))
The = doesn't have the same meaning (in the Purchase condition) as the straightforward example, but it does in the DiscountMonth condition?

2018-10-25T14:57:11.000100Z

it has the same meaning in that it requires equality, but clojure.core/= won’t be called

eraserhd 2018-10-25T14:57:29.000100Z

Not quite.

eraserhd 2018-10-25T14:58:16.000100Z

= is unification. So, it requires the expression to be true. If there are variables in it that do not yet have a value, the variables are "assigned" such to make it true. If the variables are already assigned, it works like clojure.core/=

2018-10-25T14:58:31.000100Z

That’s a reasonable way to think about it

2018-10-25T14:58:36.000100Z

but Clara may optimize on it

2018-10-25T14:58:49.000200Z

when unifying on a ? binding

2018-10-25T14:58:58.000100Z

it uses it to build the rete graph

2018-10-25T14:59:20.000200Z

> = is unification This distinction is makes sense!

1🙂
2018-10-25T14:59:23.000100Z

makes a hash-based match for nodes known to be doing = based bindings

2018-10-25T14:59:36.000100Z

but = is also just = when equating to constant values etc

eraserhd 2018-10-25T14:59:36.000200Z

If there are many possible values for a variable, it can assign each value consecutively.

2018-10-25T14:59:53.000100Z

you can do [Purchase (= ?month month "JAN")] for example

eraserhd 2018-10-25T14:59:55.000100Z

(*and what @mikerod is saying is true, I'm only describing the semantics, it doesn't actually work like that.)

1💯
2018-10-25T15:00:05.000100Z

yeah, semantics are most important

2018-10-25T15:00:25.000100Z

just making sure someone may not get carried away and try to print within clojure.core/= or something and wonder why it isn’t always called hah

2018-10-25T15:00:33.000100Z

unlikely

2018-10-25T15:01:15.000100Z

[Purchase (= ?month month "JAN")] binds the value of the field month to ?month, when (= month "JAN") (via clojure.core/=)

2018-10-25T15:01:25.000100Z

Of course in this case, ?month would just be “JAN”

2018-10-25T15:01:51.000100Z

so perhaps something a bit more useful than that 😛

2018-10-25T15:03:25.000100Z

This page on the docs might be of interest: http://www.clara-rules.org/docs/hash_joins/

2018-10-25T15:04:10.000100Z

Woah secret docs

2018-10-25T15:06:14.000100Z

“`=` is unification” => correct. Note that this is for top-level constraints though, when nested inside a constraint that isn’t the case i.e. [A (= ?b c)] vs [A (stuff (= ?b c))]

1✅
2018-10-25T15:06:46.000100Z

Hah not secret, just nested inside the “performance optimization” page

2018-10-25T15:07:27.000100Z

Or more precisely on my last, that is true if you’re trying to create new bindings - it’s still unification semantically if you’re using bindings from before

2018-10-25T15:09:17.000100Z

> [A (= ?b c)] vs [A (stuff (= ?b c))] Yes! This is what I ran into immediately, I wanted to use my own equality function, but I got pointed to using :test instead.

2018-10-25T15:10:15.000200Z

BTW thank you all for helping me understand this!

2018-10-25T15:23:17.000100Z

some of the semantics around = can get weird, since it kind of plays 2 roles depending on context

2018-10-25T15:23:27.000100Z

like when how you can’t nest it within another call to do a unification

2018-10-25T15:23:38.000100Z

but most of the time not a big deal

2018-10-25T16:55:18.000100Z

@eraserhd Agreed with @mikerod’s previous comments - Another interesting way that this played out (both myself and Mike spent a while doing perf optimizations on Clara) is that in practice, it turned out that even for large cases (hundreds of thousands of facts/tens of thousands of rules) the constant factors seemed to be most important. Hashing turned out to be a large percentage of work performed for example. A lot of these optimizations (on the Clojure side) are in the memory.cljc namespace with lots of Java interop etc. That said, this was in use-cases where we didn’t really have lots of data that we were just going to end up discarding, and there’s definitely cases where more laziness could be useful.

2018-10-25T22:01:32.000100Z

Is it possible to transform the output of defquery or would I need to do that in a wrapper function? The RHS of a defrule is just Clojure but that doesn't seem to exist in defquerys

2018-10-25T22:11:40.000100Z

@jvtrigueros defquery has no RHS

2018-10-25T22:12:11.000100Z

you can create facts in rules with defrule that do some transformations so there is less to transform once you get it from the query

2018-10-25T22:12:28.000100Z

but the query return val will just be the maps with the binding keys to the values they are bound to in the query

2018-10-25T22:17:06.000100Z

Thanks! In the query docs, it suggests one can write a query like so:

(query session get-by-last-name :last-name “Jones”)
How would one write the defquery for that?
(defquery get-by-last-name
  [:last-name]
  [?person <- Person (= last-name :last-name)])

2018-10-25T22:19:00.000100Z

I'm only able to create defquerys that have ?:

(defquery get-by-last-name
  [:?last-name]
  [?person <- Person (= last-name ?last-name)])
But then the query call wouldn't be using :last-name but :?last-name