Fork me on GitHub
#clara
<
2024-03-10
>
fvides14:03:15

Hello. I'm currently learning how to use clara to develop my first rule based system. I've got a question: what are the downsides of using maps as facts instead of record instances? It seems to me that maps are more clojure-ish and can think of a few upsides (i.e. schema validation, data generation for testing, namespaced keys, more concise). But, what are the downsides?

jgomez14:03:33

Fact fields binding seems a bit more magical with Records vs maps where you have to destructure the fields, but that’s not necessarily inherently good or bad. In my implementation I preferred to use maps vs records because they’re more portable and just simpler, there’s no performance penalty, if anything expressions using maps are more explicit so there is less discovery to do during compilation.

jgomez14:03:32

Maps with keyword fact types are also pretty flexible to represent hierarchy via derive/underive vs being locked using class inheritance.

mikerod17:03:20

I’d say records would be a bit more performant actually. But you’d have to have some heavy hitting LHS evaluation to notice either way. Probably usually not noticeable.

mikerod17:03:00

The reflection stuff is compile time. The runtime is fast. And uses actual jvm field accessors. Also the type matching dispatch would be faster jvm instance checks.

mikerod17:03:26

I think that just means faster in the alpha nodes. But likely not the place of performance oriented focus.

mikerod17:03:32

I think records were used a lot in original Clara docs etc because of the field interop being syntactically nicer looking than having to add destructuring etc. but I agree that in clj it is more idiomatic to use maps for most things.

jgomez18:03:03

Yeah the performance of one vs the other is basically no different than using maps vs Records in regular clojure code, once it all gets compiled that’s what you’re left with, you could probably do some tests using criterium if want to quantify the difference but it’s just very minimal.

mikerod18:03:26

I’ve explored keyword lookup vs field access before. Keyword access is Definitely slower. But needs to be a very hot spot to matter. Clj does try to optimize keyword call sites.

mikerod18:03:57

I think the dispatch would be the bigger thing. Especially if you were to write a slow fact-type-fn or ancestor fn.

jgomez18:03:41

Using derive/underive just works really nice with fact maps, I’ve found that to be a very useful way of expressing polymorphism of facts

jgomez18:03:14

I’m going to spend some time in the near future documenting a little better the different usage patterns and ways this stuff can be setup

mikerod18:03:32

Sure. There is a lot you can do with fact-type-fn and ancestors-fn. we used a bit of special cases for that before.

mikerod18:03:07

The default is using the default type and ancestors fn on maps which can work out of the box of course.

fvides20:03:07

Many thanks por your comments, they were helpful. @U07MK6PLN If you think that I can be of any help with the documentation, please let me know. It could be a learning moment for me.

ethanc18:03:20

i’d hazard the usage of derive/underive, as stateful actions outside the scoping of rules executions can lead to oddities/issues with the Truth Maintenance portion of clara.

jgomez18:03:23

yeah everything should be used with caution, the usage pattern we actually use involves creating a hierarchy via derive/underive and then passing the :ancestors as the ancestors-fn this way everything that is derived/underived is intended and not incidental from loading some other outside code.