precept

Preception!
seantempesta 2017-06-08T09:15:05.953832Z

Um, this idea is fascinating. I read about it last night and I’m still thinking about it. Good work guys.

2017-06-08T11:34:08.709910Z

Quick question: what’s the difference between [?edit <- [?e :todo/edit ?v]] and [(<- ?done-entity (entity ?e))]?

2017-06-08T11:35:02.720364Z

Are they just two ways of writing the same thing?

2017-06-08T11:36:38.738870Z

Also why does define use :- and rule use => as a separator?

captainlexington 2017-06-08T13:56:18.037093Z

The documentation for Precept assumes you already know Clara, and the documentation for Clara assumes you already know rules engines. Is there a good high-level, moderately technical overview of rules engines somewhere?

👍 2
2017-06-08T14:41:05.111227Z

Another quick question: Clara rules uses defrule for defining a rule, whereas Precept uses rule. What’s the reason for dropping the def, considering that there are other macros like defsub that follow the convention?

alex-dixon 2017-06-08T16:41:37.971523Z

@weavejester Sorry. We’re working on documentation. Thanks for bearing with us

alex-dixon 2017-06-08T16:42:36.992202Z

[?edit <- [?e :todo/edit ?v]] means bind the whole fact to ?edit that matches the condition ?e :todo/edit ?v

alex-dixon 2017-06-08T16:44:41.035934Z

The <- arrow function is semantically the same. In the snippet you provided it means bind the result of (entity ?e) to ?done-entity

alex-dixon 2017-06-08T16:46:02.064713Z

The function version allows a context in which to do some macro magic under the covers for you, so it expects a “special form” in the DSL. Right now that is either “entity” or “entities”

alex-dixon 2017-06-08T16:46:39.078212Z

entity will grab all the facts associated with a given eid

alex-dixon 2017-06-08T16:47:12.089615Z

entities is just the list version of that. So it takes a list of eids and will return you the entity for each one in the same order as the list you provided

2017-06-08T16:47:22.093338Z

Ah, so [?done-entity <- (entity ?e)] wouldn’t work?

alex-dixon 2017-06-08T16:47:39.099507Z

Correct

alex-dixon 2017-06-08T16:48:34.118804Z

And honestly I think we should be able to support that one because there’s not that much going on underneath the covers

2017-06-08T16:49:26.136971Z

Yeah, I was about to ask if you could; one syntax is easier to remember than two.

alex-dixon 2017-06-08T16:49:27.137076Z

That one just expands to [?done-entity <- (acc/all) :from [?e :all]]]

2017-06-08T16:49:34.139660Z

Ahh

2017-06-08T16:50:02.149729Z

Out of interest, what prompted you to use rule instead of defrule?

alex-dixon 2017-06-08T16:50:46.165862Z

At the end of the day probably because I thought it was prettier

alex-dixon 2017-06-08T16:51:23.179081Z

My justification was after writing your 15th rule you probably know it interns a var and it’s not important to communicate it with the code at that point

alex-dixon 2017-06-08T16:51:39.184942Z

Certainly after the 100th it felt that way

2017-06-08T16:51:43.186352Z

Does the idea of an anonymous rule make sense?

2017-06-08T16:52:46.209009Z

Sorry for all the questions

alex-dixon 2017-06-08T16:52:47.209515Z

I’m not sure. Clara requires rules to be interned in the ns

alex-dixon 2017-06-08T16:53:00.214016Z

No! My pleasure. Thanks for your feedback

alex-dixon 2017-06-08T16:53:37.226988Z

This morning on discord we were talking about supporting the ability to write just a LHS and reuse that across multiple rules

2017-06-08T16:53:37.227235Z

Cool. I just don’t want to make it sound like I’m second-guessing your decisions. It’s more that I want to understand them.

2017-06-08T16:54:00.235203Z

Then when I understand them… then I’ll second-guess you 😉

alex-dixon 2017-06-08T16:54:03.236445Z

Oh. Well if you are second guessing them I welcome that. But thank you 🙂

alex-dixon 2017-06-08T16:54:04.236891Z

Lol

alex-dixon 2017-06-08T16:54:22.243203Z

Please do. I like learning 🙂

2017-06-08T16:54:35.248262Z

Does rule support docstrings like Clara?

alex-dixon 2017-06-08T16:54:58.256380Z

…don’t know. It should

2017-06-08T16:56:33.291055Z

I think I tentatively prefer defrule, purely from the point of view from someone coming at the code for the first time. There are a few things in Precept that are not entirely obvious whether they intern at first glance. session is another one. I guess that interns a var?

alex-dixon 2017-06-08T16:57:01.301511Z

It does

2017-06-08T16:57:47.318379Z

Does session always take a namespace?

alex-dixon 2017-06-08T16:58:10.326380Z

Clara has mk-session which does not, defsession which does. Their Clojurescript version has defsession only, but they’re either close or already have a Clojurescript version of mk-session as of about a week ago I’ve just not had time to check it out

alex-dixon 2017-06-08T16:58:20.330329Z

Yes

captainlexington 2017-06-08T16:58:21.330806Z

Could you make the def more explicit /and/ less redundant by having a defrules form that takes in a seq of rules?

captainlexington 2017-06-08T16:58:31.334245Z

Or rather, a seq of rule-definitions

captainlexington 2017-06-08T16:58:37.336635Z

Not a seq of anonymous rules

alex-dixon 2017-06-08T16:59:50.363680Z

I’m not sure. Clara reads from the namespace and I’m not sure we could supply a list of actual rules in Clojurescript. I think you can in Clojure but not Clojurescript

2017-06-08T17:00:01.368111Z

Thanks for the info. Again, I kinda like defsession over session, as it makes it clear what it’s doing. I don’t think I’d mind the additional three characters… but maybe my opinion will change if I get used to it. These are just initial impressions.

2017-06-08T17:00:36.382267Z

On the subject of first impressions, why does define use :- and all the others use => as a separator?

alex-dixon 2017-06-08T17:00:39.383384Z

You’re making good sense to me 🙂

alex-dixon 2017-06-08T17:02:06.415866Z

Clara uses =>. rule is a Clara style rule. Prolog uses :- and define is a prolog style rule.

alex-dixon 2017-06-08T17:02:25.422690Z

Of course that’s silly. But there you go 🙂

2017-06-08T17:03:30.446677Z

The syntax for the rules is the same, right? It’s just the consequences that are different?

alex-dixon 2017-06-08T17:03:57.456343Z

The order is reversed so I think that was taken into consideration as well. Obviously that could mess up some rules

alex-dixon 2017-06-08T17:04:10.461259Z

Whether it ultimatley helps people see that quickly or remember /shrug

2017-06-08T17:04:23.465778Z

Ah, so define is “backward?” consequences first then rules?

alex-dixon 2017-06-08T17:04:34.469655Z

Yes. Sorry. Should have been more clear

2017-06-08T17:04:50.475235Z

Maybe <= ? 🙂

alex-dixon 2017-06-08T17:04:56.477053Z

Lol

2017-06-08T17:05:22.486256Z

I guess that might imply you could use that operator in both senses, though.

alex-dixon 2017-06-08T17:05:25.487455Z

It’s a tip of the hat to Prolog though 🙂 https://en.wikipedia.org/wiki/Prolog

2017-06-08T17:06:01.499850Z

Yeah, and that makes sense if you’re familiar with Prolog…

alex-dixon 2017-06-08T17:06:02.500097Z

The idea is the same. Rules are “when then”, Prolog is this is true if/when

2017-06-08T17:08:08.544295Z

(rule todo-is-visible
  [[_ :visibility-filter :active]]
  [[?e :todo/done false]]
  =>
  [:db/add [?e :todo/visible true]])

2017-06-08T17:08:39.555343Z

Would something like that make sense? So if the consequences evaluate to a vector, treat it like a Datomic transaction

2017-06-08T17:08:44.557034Z

Might be a dumb idea 🙂

alex-dixon 2017-06-08T17:09:10.566027Z

I don’t think so. Fan of Datomic 🙂

alex-dixon 2017-06-08T17:10:41.597638Z

We are doing a lot of work inside insert! for schema maintenance. Also, insert! is like Clara, so it’s insert logical. There’s also insert-unconditional!. Both behave the same as Clara with respect to truth maintenance. Precept enforces a schema inside those functions though

2017-06-08T17:11:02.604658Z

Okay 🙂

2017-06-08T17:11:45.619655Z

Anyway, I’ve yet to use this in a real project. I was going to try it on a webchat tutorial I was putting together. Those were just my initial impressions 🙂

2017-06-08T17:12:59.645416Z

At the risk of bikeshedding, I prefer Clara’s defsession and defrule, as that makes it very clear that they’re interning vars. Or if you’re not going for that, defsub kinda feels a little inconsistent?

2017-06-08T17:13:13.650313Z

define I think I could get used to. 🙂

alex-dixon 2017-06-08T17:13:23.653684Z

Haha. Yes. That bothers me too

alex-dixon 2017-06-08T17:14:30.677173Z

I guess I’m imagining a land where basically there are only rules. I think really we’d like to have everything be (define) or some version of rule that doesn’t even require you to name it. We’ve made some progress on that but it got a little tricky and I kind of just ran out of time

alex-dixon 2017-06-08T17:15:17.693892Z

So yes. I think I have a complex about how much Precept is basically Clara. They’ve done all the hard work for us, we’re adding sugar. So I was somewhat reluctant to take the way they named things too 😕

2017-06-08T17:16:33.720804Z

That makes some sense. derive is global but doesn’t def. But I think if you’re interning vars, or adding anything to a registry (like spec’s clojure.spec/def), then def is a convention that has a lot of weight behind it. Those three characters tell those new to the library a lot.

2017-06-08T17:16:53.727815Z

In terms of Simple vs. Easy, it’s “easy”, in that it’s something familiar, but the cost isn’t onerous IMO.

alex-dixon 2017-06-08T17:16:59.729879Z

Yep. I knew I was breaking the Clojure convention

2017-06-08T17:17:05.731905Z

That said, maybe I’ll change my mind after the 25th rule I write 😉

alex-dixon 2017-06-08T17:17:09.733386Z

Lol

2017-06-08T17:18:29.760699Z

I’m probably pickier than most about syntax, though! I’m currently writing a small websocket library because I couldn’t find one I liked.

alex-dixon 2017-06-08T17:18:39.763943Z

Yeah. Not sure if it’s coming across but…We really only want to deal with rules, or things like rules,. Like prolog it’s almost like we don’t even want to write rule…we know we’re writing a rule, that’s all there is 🙂

alex-dixon 2017-06-08T17:19:12.775786Z

I hear you on that one heh. And yeah…I’m the exact same way

alex-dixon 2017-06-08T17:19:25.780394Z

I forget which websocket library I settled on…

alex-dixon 2017-06-08T17:20:16.798305Z

sente

2017-06-08T17:20:57.812783Z

I think if the rules were anonymous it would make more sense, like derive. Or if the rules weren’t assigned to a registry, but were data you could pass around. But there are limits to what can be done with that.

2017-06-08T17:21:29.824056Z

I looked at Sente, but got picky about its syntax 🙂

2017-06-08T17:22:03.835607Z

That all said; there’s a lot that looks very promising about Precept. I’m looking forward to using it!

alex-dixon 2017-06-08T17:22:21.842273Z

Thank you. Can’t tell you how much I appreciate your feedback

2017-06-08T17:22:23.842989Z

I’m giving you my initial criticisms, but everything else I like.

alex-dixon 2017-06-08T17:22:43.849826Z

That’s good to hear also 🙂

2017-06-08T17:23:05.857504Z

I think you mentioned in your README that you wanted to use it for games as well?

2017-06-08T17:23:48.872557Z

I found when writing Ittyon that an EAVT architecture was good in that case. Knowing when a rule occurred is important in realtime games. But maybe the EAV facts are more general-use.

alex-dixon 2017-06-08T17:24:39.889972Z

Yes. Games and game-like UI is one of if not the major motivation

2017-06-08T17:24:55.895486Z

Another random idea: maybe some kind of index that updates according to rules.

alex-dixon 2017-06-08T17:24:59.896809Z

But we do a lot of UI work at our company and we’ve always wanted programming to be more like this

2017-06-08T17:25:31.908084Z

In Ittyon, each time a fact is added to the database, indexing functions can be registered that construct data structures for fast referencing.

alex-dixon 2017-06-08T17:26:10.922041Z

Hm. That sounds a bit like Rete?

2017-06-08T17:26:52.936785Z

So by default it has eavt, aevt and avet indexes, but it’s often useful to add in more specialised indexing. e.g. proximity lookup based on quadtrees.

alex-dixon 2017-06-08T17:27:09.942574Z

Ah! Ok

2017-06-08T17:27:35.951935Z

I think there’s a limit to what one algorithm could do, even one like Rete…

2017-06-08T17:27:58.960173Z

Anyway, that’s just something I found with Ittyon, which is a EAVT-based database for game dev.

2017-06-08T17:28:19.967680Z

It’s rule engine is far simpler though - just reactions to single rules.

2017-06-08T17:28:33.972907Z

Precept/Clara are far more sophisticated in that respect.

alex-dixon 2017-06-08T17:28:38.974672Z

There is. Hence quadrees on the roadmap, other general purpose algorithms. Our thinking was to try to use implementations of those algorithms out of the box

2017-06-08T17:29:00.982455Z

It would be nice if you had a way of hooking in custom indexing.

alex-dixon 2017-06-08T17:29:14.987355Z

We do have eav and aev indexes in the schema layer for enforcing cardinality and uniqueness

2017-06-08T17:29:15.987601Z

IMO indexing is super important for games based around fact databases.

alex-dixon 2017-06-08T17:29:48.999366Z

Ok. And you’re thinking for performance or convenience? Both?

2017-06-08T17:30:02.004127Z

Performance.

2017-06-08T17:30:43.019772Z

I found most performance problems were related to indexing.

2017-06-08T17:30:53.023562Z

But I’m just one datapoint 🙂

2017-06-08T17:31:06.028301Z

Or could be solved by better indexing.

alex-dixon 2017-06-08T17:31:36.038591Z

There’s certain situations where Clara/Rete don’t give us that, but in most cases it does in a pretty optimal way. Especially for games where you’re dealing with a lot of incremental changes

2017-06-08T17:31:58.046752Z

Incidentally, Precept might hook into Impi well: https://github.com/weavejester/impi

alex-dixon 2017-06-08T17:32:10.051386Z

Oh nice!

2017-06-08T17:32:26.057087Z

Anyway, thanks for answering my questions and suffering through my criticisms!

2017-06-08T17:32:32.059342Z

I need to go eat, now!

alex-dixon 2017-06-08T17:32:44.063821Z

My pleasure. Thank you 🙂

2017-06-08T17:39:18.207107Z

> Precept models state as a graph. We can add new facts about the world without concerning ourselves about its location in an object. We can query data and perform derived computations on it just as easily. Is there any thought to adding the option to expressing the apps state transtions as state charts. possible using statly? https://github.com/nodename/stately

2017-06-08T17:39:46.216810Z

Does that idea even make sense? 🙂

alex-dixon 2017-06-08T17:40:58.242875Z

Hi. Not sure. You’re thinking as an implementation or a tool to visualize state changes?

2017-06-08T17:53:42.524689Z

@alex-dixon The way i understand precept is Precept is a rule engine where: if condition then apply rule. But what if the condition is dependent on other conditions being true? So it depends not only on the current input, put past inputs and what state were currently in. I believe its possible to model this as a FSM. But apparently its very cumbersome. StateCharts are an abstraction overtop of FSM to allow for some re-use. So it occurred to me that you could build a StateChart model for Precept

captainlexington 2017-06-08T17:54:38.544882Z

@drewverlee So as something that would help a developer understand their app as they were making it?

2017-06-08T18:01:46.710571Z

@captainlexington At this point i’m just wrapping my head around What Precpt is, i’m vaguely familiar with the idea of a rules engine and i’m trying to work though how you would structure an application using it. The first hurdle i had was trying to see how you would achieve different results depending on pre-existing inputs to the system.

alex-dixon 2017-06-08T18:13:52.983582Z

@drewverlee oh ok. I think you'll find Precept handles that case rather well. 😊

alex-dixon 2017-06-08T18:14:52.005154Z

You should be able to insert facts that are preexisting, no?

alex-dixon 2017-06-08T18:16:15.035482Z

Rules have no ordering. Does this answer your other question?

alex-dixon 2017-06-08T18:16:53.049164Z

Also a rule may have multiple conditions

alex-dixon 2017-06-08T18:19:53.112426Z

We're working on docs ATM. In the mean time you might check out Clara's examples to see a given rule can respond to facts that may have been inserted by a chain of rules

alex-dixon 2017-06-08T18:20:30.125967Z

https://en.m.wikipedia.org/wiki/Forward_chaining

kenny 2017-06-08T18:33:43.415563Z

I also agree with @weavejester on the def convention. It wasn't immediately clear to me that rule was interning a var until I looked at the impl. But again, I am just now taking a look at this project so my opinion may change as well 🙂

kenny 2017-06-08T18:38:15.514088Z

Also I think the example project would be much easier to read if vars weren't :refer'ed and an alias was used instead.

alex-dixon 2017-06-08T19:05:21.101407Z

@kenny Valuable feedback. Thank you 🙂

jfntn 2017-06-08T19:08:03.155306Z

Curious about the relationship with statecharts too. I’m not familiar with the clara rules engine, but it seems that rules would be a great way to express transitions between states. What seems to be missing afaict is the ability to dynamically load rules as the state changes?

alex-dixon 2017-06-08T19:12:51.250263Z

@jfntn Correct. If statecharts could solve that we’d definitely be interested in taking a look. It’s very likely we will want different rules to be loaded in different contexts for performance reasons

alex-dixon 2017-06-08T19:13:46.268120Z

Left hand side calculations may run in some contexts for which they are not relevent as an artifact of Clara’s implementation that allows parallelization

jfntn 2017-06-08T19:14:20.279426Z

Right, the current state should determine what rules are running

jfntn 2017-06-08T19:15:25.301417Z

I think you mentioned earlier that this isn’t yet possible for clara in cljs, is that correct?

mikegai 2017-06-08T19:25:31.500959Z

@jfntn Yes, Clara is fairly eager in its processing (from what we can tell) so if you have rules in your session they will do some work even if you gate them with a controlling fact (e.g. make the first pattern something like [_ :current-state :firing]. Clara has a concept of activation groups but currently those control salience and do not gate processing. Other rete-ish algorithms (like Drool's phreak algorithm) are much more lazy and can be gated performance-wise so you can keep all the rules in one session and still have strongly bounded contexts.

mikegai 2017-06-08T19:26:23.518159Z

(I'm at CoNarrative with Alex btw)

mikegai 2017-06-08T19:28:44.565268Z

And yes rules are definitely a perfect way to transition between states.

mikegai 2017-06-08T19:36:08.712529Z

So one way we're using Precept today is keep the "current state" of a finite state machine (more or less) in one global app session. We use file structure to bundle edge transition decision rules how we want. But all the rules for the various states are already compiled in the one session; just not activatable untl you're in that state.

mikegai 2017-06-08T19:43:57.862243Z

So the "current state" does effectively determine what rules are running -- there just may be some background overhead.

mikegai 2017-06-08T19:47:37.933526Z

So far that hasn't been a problem for us. But we're keeping an eye on it! If it does, we have a couple of future strategies in mind. The easiest is to maintain multiple sessions each with their own set of rules. Say at the "top route" level. On transition, we'll just "pour" base/ground/underived facts from the current session state into the new session. Within that context now only the relevant rules will be fired, but it'll have "memory" of its common state.

mikegai 2017-06-08T19:48:11.944555Z

A more elegant way will be to work on it at the Clara engine level. Kudos to them by the way -- none of this would be possible without their work!

mikegai 2017-06-08T19:58:08.145730Z

@weavejester : We’re actually using Precept as the basis for a web app that has very dense, visual, game-like UI with lots of real-time decision logic (along with lots of more conventional web UI). Think live UI decisions during a drag operation with potentially many, many elements on canvas. (btw @jfntn we model the various states of the various mouse operations as state machines and use rules to transition between them.)

mikegai 2017-06-08T20:01:16.211355Z

To do some of our collision/intersect detection efficiently, we are looking at using quad-trees (and r-trees because much of our data may be irregularly-sized & nested) to inform the rule engine about proximity. In that use case, the relationship between “quad-tree” and Precept is somewhat like between a game engine and a Lua-based scripting language; the scripting engine isn’t necessarily doing all the heavy number-crunching but receiving synthesized inputs over which you can express complicated logic.

kenny 2017-06-08T20:31:52.820293Z

Have you guys given any thought to best practices when interacting with external resources (e.g. a server). The obvious way would be to have a rule execute when a certain interaction occurs (e.g. user clicks a button) with the RHS sending a HTTP request to a server and insert'ing or retracting based on the response.

mikegai 2017-06-08T20:33:34.854293Z

@kenny Yes, we're working on some examples along those lines right now!

mikegai 2017-06-08T20:34:04.863906Z

In short, the response is always going to insert-unconditional or retract

mikegai 2017-06-08T20:35:25.891039Z

how to trigger the HTTP request itself (the side-effect) we are of two minds. The simplest way (and what we're doing today) is what you just said: fire from the RHS/consequence.

mikegai 2017-06-08T20:41:39.013445Z

A more purely functional way would be for the RHS to just insert a fact representing a side-effect intent (like a HTTP request). That way fire-rules wouldn't actually trigger the side-effects, but calling infrastructure could process the "side-effect queue" instead. We're not currently experiencing any pain points around the simple "do it in the RHS" way though.

mikegai 2017-06-08T20:43:17.045373Z

That may be because mostly we build apps that are more 'fire-and-forget' message-driven. Send off some message to the server; one or more future messages get pushed back. The best practice there is simple -- just put them in the action queue like something coming from a UI interaction.

mikegai 2017-06-08T20:43:58.058307Z

"them" being messages from the server

kenny 2017-06-08T20:57:11.319886Z

Right, ok that makes sense. Let me walk through a simple example and see if it makes sense to you. Let's say we are building a login form with an email and password. You will need some subscriptions for the email and password:

(rules/defsub :email
  [[_ :email ?email]]
  =>
  {:email ?email})

(rules/defsub :password
  [[_ :password ?email]]
  =>
  {:password ?email})
And some basic UI:
(defn Login
  []
  (let [{:keys [email]} @(precept/subscribe [:email])
        {:keys [password]} @(precept/subscribe [:password])]
    [:div
     [:input {:type      "text"
              :value     email
              :on-change #(precept/then [:global :email (.. % -target -value)])}]
     [:input {:type      "text"
              :value     password
              :on-change #(precept/then [:global :password (.. % -target -value)])}]
     [:button {:on-click #(precept/then [:global :logging-in? true])} "Login"]]))
And finally the important rule:
(rules/rule
  logging-in
  [[_ :logging-in? true]]
  [[_ :email ?email]]
  [[_ :password ?password]]
  =>
  (do-login ?email ?password
            (fn [response]
              (if (= (:status response) 200)
                (precept/then [[:global :logged-in? true]
                               [:global :logging-in? false]])
                ;; handle errors...
                ))))

mikegai 2017-06-08T21:01:01.395358Z

@weavejester and anyone else interested in how Precept might interact with external indexes and calculations (physics, quad-trees, etc.): https://github.com/CoNarrative/precept/issues/67

mikegai 2017-06-08T21:04:11.457882Z

@kenny Yes, that's exactly it 🙂

mikegai 2017-06-08T21:05:14.477706Z

Technically you currently don't need to use precept/then in the consequence and can just use insert-unconditional -- but using precept/then is better future-proofing as it queues it up for next tick and we might enforce that flow at some point.

mikegai 2017-06-08T21:06:15.497213Z

Also, an alternate is to insert the status & data directly into the rules, and then have other rules do the handling of each possible success/error case.

mikegai 2017-06-08T21:06:53.509001Z

Basically, any time you see an "if" in the RHS/consequence it might be a signal to insert for downstream rule processing (you'll have lots more logical flexibility as it's what rules do 🙂 )

kenny 2017-06-08T21:07:24.518230Z

Right, I was thinking that but this is all new to me so I was unsure.

kenny 2017-06-08T21:13:35.630088Z

BTW do :global and :transient have a special meaning for the e? I see it used in the example but it's not clear what they mean.

mikegai 2017-06-08T21:14:16.642045Z

:global is not treated specially and just a convention to say "hey, I'm a singleton!"

mikegai 2017-06-08T21:14:29.645648Z

You can make up whatever entity ids you want

mikegai 2017-06-08T21:15:13.658744Z

:transient does (currently) have special treatment. Any eav tuple with an entity id of transient gets cleaned up at the end of a fire-rules

kenny 2017-06-08T21:15:35.664923Z

Gotcha. Technically you could use any keyword but :global is just the convention.

kenny 2017-06-08T21:15:49.668994Z

Cleaned up means retracted?

mikegai 2017-06-08T21:15:54.670360Z

yes, sorry 🙂

mikegai 2017-06-08T21:18:17.711728Z

:transient itself is a very lightweight concept and consists of a single rule:

mikegai 2017-06-08T21:18:33.716096Z

(clara.rules/defrule clean-transients___impl
  {:group :cleanup}
  [?fact <- :all (= :transient (:e this))]
  =>
  (clara.rules/retract! ?fact))

kenny 2017-06-08T21:19:12.726750Z

Nice and simple.

mikegai 2017-06-08T21:20:45.753737Z

Someone could call it :action or :command (that might better communicate what it's for) and add the same rule for that entity id, or if you wanted to queue commands in the session you could generate separate entity-ids for each command (and mark them in some way for cleanup, or keep them around for a command history, etc.)

kenny 2017-06-08T21:21:25.764986Z

This leads to my next question of the meaning of {:group :action}, {:group :cleanup}, etc. At first I thought they were for documentation purposes but now it seems like it has some effect on rule ordering?

mikegai 2017-06-08T21:21:54.773241Z

yes, that's from Clara and they're called "activation groups"

mikegai 2017-06-08T21:23:09.794285Z

In precept.core:

(def groups [:action :calc :report :cleanup])

mikegai 2017-06-08T21:25:12.828854Z

default rule flow goes in that order -- handle mutations from incoming actions, derive calculations, hydrate maps for the view, then cleanup transients

mikegai 2017-06-08T21:25:24.832311Z

(the action parameters that just came through the pipeline)

mikegai 2017-06-08T21:27:23.865064Z

You can roll your own flows too -- that's just the default we provide. In Clara, activation groups don't prevent rules from firing (like "agenda groups" in other rule engines which act as states in a fsm). Clara activation groups just control salience, that is, which rules are going to be fired first.

kenny 2017-06-08T21:29:05.892760Z

Gotcha. Counterintuitive name.. 🙂

kenny 2017-06-08T22:53:59.033922Z

If I insert a fact that look like this [:transient :my-map {:foo 1 :name "Bob"}], is it possible to write a rule that uses the map? Something like this:

(rules/rule my-map-one
  [[_ :my-map ?m]]
  [[?m :foo 1]]
  =>
  (println "One!"))
(above code doesn't work)

mikegai 2017-06-08T23:52:19.610892Z

@kenny rule engines (Clara certainly upon which we're based) typically model everything in a flat/relational graph manner instead of using nesting. If there really are child/related entities, insert them separately and join. So one way to express the above would be as a "join" between the :transient entity and the :map entity:

mikegai 2017-06-08T23:52:33.613099Z

[:transient :my-map 43]
[43 :foo 1]
[43 :name "Bob"]
[43 :transient true]

mikegai 2017-06-08T23:52:58.616861Z

or, because I think I want :transient renamed (or just a convention)...

mikegai 2017-06-08T23:53:03.617632Z

[:this-command :my-map 43]
[43 :foo 1]
[43 :name "Bob"]
[43 :transient true]

kenny 2017-06-08T23:53:21.620458Z

Totally makes sense. Are there any utility functions to make this easier? Or is this not a common enough pattern?

mikegai 2017-06-08T23:53:38.622988Z

if that latter makes it clearer. The :transient attribute is just there to mark the related entity for cleanup (you'd have to add a rule for that, but simple enough)

mikegai 2017-06-08T23:54:02.626438Z

No, it's common and we have some helper utilities. I'll defer to @alex-dixon on those -- @alex-dixon ?

kenny 2017-06-08T23:55:27.639134Z

precept.util/tuplize-into-vec looks like it

kenny 2017-06-08T23:55:39.640933Z

(precept.util/tuplize-into-vec {:db/id 1
                                :a "a"})
=> [[1 :a "a"]]

mikegai 2017-06-08T23:56:22.647657Z

I'm wanting as much inter-op between maps and eav tuples as possible - just two ways of looking at the same thing and sometimes one is better than the other for a particular case. The core representation will remain eav tuples under the cover of course.

kenny 2017-06-08T23:57:45.660260Z

And that makes sense. I suppose I was trying to mimic Datomic's tx-data. i.e. You can pass datoms or a map. If passed a map, then Datomic will turn it into tuples.

mikegai 2017-06-08T23:59:37.676939Z

https://github.com/CoNarrative/precept/issues/28

alex-dixon 2017-06-08T23:59:44.677987Z

😊