Fork me on GitHub
#clara
<
2022-03-04
>
apbleonard12:03:35

Is this a valid way of looking for Foo and Bar facts that don't have the same :x field ? Is there a "better" way?

(defrule my-rule
  [Foo (= ?x (:x this))]
  [Bar (not (= ?x (:x this)))]
  =>
  (insert (->Baz)))
I find it a little hard to parse the use of (not ...) as a piece of logic together with (= ...) which is here only used for assignment to try and unify facts with the same :x .... if you see what I mean? Also interested in what happens and how it should be understood when the :x of Bar happens to be nil - which is partly what prompted this question 🙂

mikerod01:03:33

If :x is nil, then it’d just try to unify on nil.

mikerod01:03:08

In terms of how to write this rule, I don’t think I immediately see an issue with what you proposed.

mikerod01:03:03

Actually, you may want to use not=. I’m not sure unifying makes sense if you then wrap a not on it. I forget if the compiler is capable of understanding this in a meaningful way. Does it behave as you expect?

apbleonard21:03:11

Well the rule was meant to fire to show a problem when the user had supplied an (Foo) :x which was different to the "required" (Bar) :x , which it did - including when the supplied :x was nil. We weren't expecting that the supplied :x could ever be nil but that was our fault. But on trying to solve that bug I started squinting at this code and realising I didn't reallyknow why it was working at all. I must admit I wasn't aware of not= nor can I find docs about it? Seems in theory a tricky thing to achieve - a kind of complement to unification?

mikerod21:03:35

not= is just clj function. Not special to Clara.

mikerod21:03:48

= is a bit special since Clara uses it for unification

mikerod21:03:14

So in this case I think I'd avoid the unification and just use a “clj function filter” which not= would serve the purpose

mikerod21:03:28

If you didn't want nil. You could add a constraint for some?

apbleonard17:03:24

Ah ok. So I guess we should be doing this. That reads better to me as I'm not left scratching my head about whether it's unifying or not 🙂

(defrule my-rule
  [Foo (= ?p (:x this))]
  [Bar (= ?q (:x this))]
  [:test (not= ?p ?q]
  =>
  (insert (->Baz)))
Also I've just now come across the destructuring part of your docs and so looks like the (:x this) parts could be cleaned up too 😊

apbleonard17:03:45

Would have to add the "some" constraint as you said. BTW I wasn't sure what you meant by "clj function filter"? (That's what had me looking in the docs again.)

mikerod17:03:32

Your rule above looks like a contradiction.

mikerod17:03:42

That's why I was thinking you should avoid unification.

mikerod17:03:01

Unification means they are equal. Then you check them not=

mikerod17:03:15

I don't see how you can unify with constraints on not=

mikerod17:03:34

You could use negation conditions to do what you want but I'm not sure what you need from the facts that you bind

mikerod17:03:55

[Foo (= ?p (:x this))]
[:not [Bar (= ?q (:x this))]]

mikerod17:03:35

However this may be a bit different than the RHS firing pattern you wanted. This would be true for every Foo that didn't have any Bar with the same :x

mikerod17:03:41

So not quite the same I think

mikerod17:03:43

Rule condition pattern is of the form

[FactType (<constraints>)]
I was just saying that constraints are like a “filter” over the facts of FactType

mikerod17:03:55

And most things you write as constraints are regular clj functions

mikerod17:03:20

Unification is a little special

mikerod17:03:33

I believe when you have a top-level = in a constraint it is treated as a unifying bind. If you meat the = below something like not in your original rendition of this, I think the unification isn't handled specially and instead the constraint expressions are just evaluated linearly across the facts of FactType like a filter. So your original version probably did work and I misread. However I still think not= is a bit more concise and clear and you still need the some? check for your nil handling.

apbleonard12:03:21

So I had never realised you could use a bound variable in ordinary LHS fact predicates before. It does say that you can in the http://www.clara-rules.org/docs/expressions/ docs page ...

apbleonard12:03:33

... where it says "The fact type is then followed by zero or more S-expressions which can either be predicates run against a fact, or bindings, as described above." But I'm afraid I missed that.

apbleonard12:03:04

In the same docs it also says below "If the same binding is used in multiple conditions, Clara ensures that all conditions can be satisfied with the same binding before activating the rule or query", except that it doesn't when you use it in a predicate s-expression after the fact type as described above.

apbleonard12:03:26

There are no examples in that page of using an "earlier" (although the order doesn't matter) bound variable ?x in a predicate s-expression after the fact type such that the ?x used in there is not unified with the others. I think there perhaps should be? Been using Clara for 4 years and we've been missing out on that option for expressing rules! 🎒

wparker17:03:49

A bit late here, but I'm not sure what scenario you're referring to @U3TSNPRT9 . Do you mean something like this?

`(defrule insert-cold-temperature
  [Temperature (= ?temperature temperature) (< ?temperature 30)]
  =>
  (insert! (->Cold ?temperature))) 
`

👍 2
wparker17:03:53

The code formatting doesn't seem to be working on my mobile unfortunately. Or did you mean something else?

apbleonard16:03:01

Pretty much. I think an example where the property of one fact is directly compared to the property of another without unifying would be useful. Like ...

apbleonard16:03:14

(defrule ice-lolly
  [Drink (= ?freezing-temp freezing-temp)
  [TempReading (< temperature ?freezing-temp)]
  => (insert! (->FrozenDrink))

mikerod16:03:46

That's a common pattern. Surprised if no examples documented

apbleonard16:03:04

Could be wrong but I had a good look in the examples too but didn't spot any.

mikerod02:03:43

Seems like it'd be a good addition then.

mikerod02:03:14

If you look through the test-rules ns in the Clara test directory you'll probably see quite a few different rule patterns that aren't in examples

mikerod02:03:37

Though some important and typical ones like the above should be documented more.