A Digression Through Recent History

The presentation so far has led with the concept of R³, with forward-chaining rules used as a tool to implement the concept. The path on how we arrived here was actually somewhat the opposite, and may be useful to review to gain some deeper insight. It's possible this sort of thing is only interesting to me, because I'm the only one in my head. You've been warned. The tl;dr is this: R³ was not a concept which emerged unbidden from my twisted mind, but rather was a solution for writing a UI application using forward-chaining rules, applied in their logically most rigorous form, and because it proved itself under those constraints, seems a promising path toward higher-quality code.

In the course of previous work, I had banged out a couple of attempts at web client "frameworks". Each attempt solved certain pain points, but ultimately proved to generalize poorly, with all roads leading to the all-too-familiar headaches of exponential incidental complexity. The ideas behind the precept project were compelling, particularly the notion of replacing the usually disjoint business logic and business data with a single rulebase, guaranteeing logical consistency. Soon after I discovered FactUI, another project with the similar goal of using rules. Seemed like a pretty good idea, given the type of problems we usually encounter in scaling our frameworks to real-world applications.

Mostly as an intellectual exercise, I wanted to see what happened if I took this idea to the extreme. Systems like clara-rules generally include mechanisms such as "rule salience" as a nod to practicality, backdoors that let you impose meta-constraints of a sort to make it easier to actually implement a system without the absolute rigor demanded by pure logic. The real-world use-cases of such rule systems often involve very large rule sets, potentially numbering in the hundreds of thousands, and giving up a bit of rigor in exchange for having a functioning system is a reasonable trade-off. Another example employed by both precept and FactUI is transient facts, facts which can be inserted, trigger rules, and then automatically retracted. Transient facts are used to handle situations like click events triggering subsequent processing, since it isn't obvious how to model "click" as a persistent fact. Anyway, I wanted to see what happens when you forego all such mechanisms, and force yourself to stick to pure logic; along similar lines, this meant avoiding use of effects in the RHS of rules, other than those which updated the session.

The first cut was an implementation of TodoMVC, building on the re-frame implementation. The ideas in Staltz' Unidirectional User Interface Architectures made sense, re-frame felt like the best-in-class example in the Clojure ecosystem, and so a reasonable place to start. The first cut had no rules at all, initially a bit surprising, but after some thought it made sense, because TodoMVC really has very little logic to speak of (which is why it's such a crappy test case for your shiny new MVC framework). Examination of the code showed two places (example) where conditional rendering was arguably done in terms of business logic, as opposed to being view-specific. The examples were pretty trivial, but in the interest of purity I made them into rules, increasing the total to a whopping two. Along the way I implemented mechanisms similar to re-frame's events and subscriptions. Events were pure data which ran through a router of sorts which interpreted them to make modifications to the rules session, updating the state. On the other end I had a "listener" attached to the session which published the changes in query results for all queries. That in turn went to another sort of router, which transformed the query-results into effects like rendering. The whole thing was wired together using transducers and core.async, and seemed very slick.

But TodoMVC was far too simple, and with only two rules clearly wasn't pushing the boundaries set by my self-imposed constraint to stick to pure logic in the rules. So I set about implementing my own "Real World" client, again drawing on a re-frame implementation as a starting point. And that's where things got interesting, and more than a little mind-bending. I attacked what I deemed to be the "hard" problems first. For instance, the Real World project is nice enough to host the REST API for the server-side component, and that server is also nice enough to do things like deliver responses out of order with respect to their associated requests. So there were annoying/helpful race conditions where the logical consistency approach would provide leverage.

Worked fantastic. The problem was that once I knocked out the hard problems and moved on to the "easy" ones, they turned out to be not so easy within the paradigm I had developed. I'm talking about really dumb things, like deleting an article, or handling a login/logout. The constraint of logical rigor inevitably led to logical contradictions, which generally manifest themselves in one of two ways: either a fact inserted by a rule vanishes, or the rule-engine gets into an infinite loop of conditional insertions and retractions. Back to the drawing board.

The turning point came while considering what really worked well, which was the HTTP request/response handling. The pain I was hitting was the sort of thing that transient events in precept and FactUI were supposed to solve, yet I didn't need that construct for HTTP. And was there really any logical difference between making an async request to a server and displaying a button with a click-handler attached? Seemed worth a try, and it quickly became apparent that the request/response paradigm could easily handle the issues I was encountering. It also became apparent that the whole idea needed some infrastructural help, so I moved back to TodoMVC to polish up the concept and implementation. Along the way, a bunch of incidental complexity fell away: events, subscriptions, transducers, the use of core.async. The results can be seen here, and just for giggles, here's the video of the simulation test of the R³ TodoMVC implementation.

results matching ""

    No results matching ""