Fork me on GitHub
#re-frame
<
2019-05-15
>
Drew Verlee00:05:10

Does reframe draw from similar ideas to those in rules engines? I feel like they both primarily derive there model from modular reactions to changes in state.

Drew Verlee00:05:40

I'm just looking into rules engines, so maybe I'm seeing something that isn't there simple_smile

dominicm00:05:20

Everything looks like a rule engine problem in clojure, can't explain why

Drew Verlee01:05:56

hmm i would ask you to explain but you cant 🙂

dominicm01:05:54

When I've looked at rule engines, I start seeing them as the solution to everything.

dominicm01:05:10

Maybe it's because they let me think in separate terms that are easier for me?

Drew Verlee02:05:58

Sure, I think I understand. It would seem to be a way to organize your code around the state. I think I'll need to have a better grasp of it in order to compare it to general programming

rickmoynihan08:05:21

> When I’ve looked at rule engines, I start seeing them as the solution to everything. They’re not 🙂 Don’t get me wrong rule engines are cool; and the right tool for more jobs than they’re used for — but they also have their problems.

dominicm08:05:32

I completely agree :)

rickmoynihan08:05:38

One maintainability issue is classically horn clauses with strict implications aren’t great at representing exceptions to rules. For example to handle an exception you usually end up with every exception to X copied across every body involving X. Then if you have several exceptions they become hard to manage because of the duplication. Years ago I left university to join a spin out that had solved this problem; but we failed to find a market.

Drew Verlee19:05:24

@U06HHF230 thanks! Can you point me to any resources that talk about this more? I don't quite understand what your saying, but that could be because I don't understand the fundamentals. So a rule could be something as simple as "A :- B" So an expectation would be, I'm not sure how to write this A and not C :- B. Where "not C" is the expectation?

rickmoynihan21:05:41

There’s a lot of subtlety to the fundamentals, and I’m far from an expert so take what I say with caution as it’ll probably be wrong, or not universally true. Negation is really a whole other topic, because it can profoundly affect the type of logic you have, and introduce problems of its own. For example many logics only work monotonically, meaning that you can’t unconclude something later based on new evidence. i.e. all facts and their conclusions are always assumed to be true, and adding a new fact or rule can’t remove or disprove an existing conclusion. The common way to handle negation is known as Negation As Failure (NAF), and it’s how you’d do it in Prolog. However NAF essentially forces you to use the impure feature of Prolog ! (cut) which stops backtracking. To implement NAF you’d do something like:

neg(Goal) :- Goal, ! , fail.
neg(Goal) .
However the cut ! is often problematic because it stops backtracking, and essentially then enforces an ordering across your rules. Where as if you were to write a program without a cut, you can write your program in order at all and it will work the same (which is desirable).

rickmoynihan21:05:30

Essentially in Prolog there are two ways to reason about any program… procedurally, or declaratively and using ! blows away half of your reasoning and leaves you only with the procedural way… and having to fight ordering dependencies across rules.

rickmoynihan21:05:14

Anyway NAF is normally supported directly in Prolog as \+ — but underneath it’s the same as the above.

rickmoynihan22:05:15

Anyway this is a big digression, as it says nothing of the exceptions you asked about.

rickmoynihan22:05:27

The classic example of the kinds of exceptions I’m talking about are expressing things like “All birds fly, except penguins”. In a monotonic logic you can’t do that.

rickmoynihan22:05:20

As the exception would refute the rule “All birds fly” under strict implication… essentially the :- (if) in something like Prolog.

rickmoynihan22:05:18

So you need a different type of logic - a non-monotonic, defeasible logic. Defeasible essentially means defeatable, i.e. the exception can override (defeat) the rule. So essentially we implemented := which was like :- but you’d read it as “it’s usually the case” instead of if. So you’d write :

flies(X) := bird(X) .
bird(tweety).
Then later might learn that:
penguin(tweety).
notflies(X) :- penguin(X).

rickmoynihan22:05:01

Anyway I didn’t have much at all to do with the underlying logic… that was all done by our CTO Chris Reed and based on the mathematical logic of Henry Prakken.

Drew Verlee05:05:43

Thanks!! I'll get to reading this tomorrow!

Drew Verlee05:05:53

Wow, thanks. I feel like this is helping me reframe things in a new light. There is a take interesting take away here that I'm right on the brink of seeing.

dominicm06:05:08

Your example didn't say much about needing to copy and paste. You can "refactor" around this example as long as you control the rule which inserts whether you fly or not.

rickmoynihan09:05:44

Sorry… To take it further to explain that… you need to add another rule that uses the flies predicate… e.g. all things that fly are awesome…

awesome(X) :- flies(X).
Except without something like defeasibility, you now need to either propagate the exception that penguins don’t fly to the awesome predicate, e.g.
awesome(X) :- \+ penguin(X), flies(X).
In this case I suppose it’s ok, because penguins happen to be awesome anyway 🙂 but they’re not awesome because they fly 🙂. Or you need to change flies(X) to be a long list of exceptions, which works for simple cases; but what do you do when you have exceptions to exceptions etc? It gets messy quickly, without a form of default reasoning. There are workarounds to these issues, but my understanding is they’re not great. I don’t know of any reasoners other than ours that do this; though I’m by no means up to date with the state of the art, and unfortunately ours was closed source and died with the company.

dominicm09:05:18

But penguins can't fly, so they aren't covered by that rule.

rickmoynihan09:05:23

If you write flies as a long list of exceptions — which was the alternative above. Think of it like the difference between case and defmethod, it’s not open for extension. Also as I was trying to say negation has peculiar semantics due to the difference between not knowing something and knowing something is false. It also destroys the purely declarative nature of the rules — which is kinda why you’re using rules in the first place 🙂

mikethompson02:05:13

@drewverlee I'd love to add a rule-based aspect to re-frame. Been dwelling on that one for a while. But, no, I don't think there's much there currently which is rules oriented. Its currently all about data flow.

👍 4
Drew Verlee16:05:47

Thanks mike, The more i read about a rules engine the more i feel its similar in nature. Can you clarify how you think its different? I have no real critique or goal in mind, just learning 🙂

mikethompson00:05:08

At a deep enough level, its all events, logic and then changes. So everything is like everything else at a deep enough level. But at a more shallow level, there are no "rules" in re-frame, so, no, I don't see it. But there is data flow, some of it reactive, so I find that a better lens.

Drew Verlee00:05:10

Thanks again, that makes sense to me.

mikethompson02:05:20

If you want to see rules used to good effect, look at precept

lepistane06:05:37

how do i detect if user clicked outside the element?

dominicm09:05:28

Add an event listener for clicks on window, then check the target

dominicm09:05:48

There's a isolated implementation on http://usehooks.com you could copy

lepistane10:05:47

i added event on toplvl component with stop propagate where i need it to behave differently but tnx i will convert it to cljs

bfay15:05:20

Kinda a vague question, but I have a bunch of divs scattered around the screen, and I'm connecting some of them together with svg bezier curves. To figure out the coordinates to use for the svg lines, I need to know information about the size of these divs. I'm accomplishing this by adding a :component-will-mount handler to the divs, which dispatches an event with information about the size of the dom node. It works fine, but I'm wondering if anybody has done anything similar, and maybe come up with a cleaner solution?

lilactown15:05:28

I think that you would typically want to use :component-did-mount?

lilactown15:05:57

reason being is that you want to ensure that the div has actually been inserted into the page before you measure

bfay15:05:48

ah whoops you're right, I think I did use :component-did-mount

lilactown15:05:30

are you using refs?

bfay15:05:38

No, is that a react concept?

lilactown15:05:26

it depends on what you need. componentDidMount will run the first time the component is created, but will not on subsequent renders

lilactown15:05:47

if your divs stay the same size, I think that’s OK?

bfay15:05:10

They're... probably going to stay the same size, but I figured if they changed I would add something to componentDidUpdate

lilactown15:05:27

you could also use a callback ref to get a reference to the dom node and do your calculations / trigger an event in there

bfay15:05:38

I think I like that idea

bfay15:05:53

had kinda wondered if I could just store the dom-node I get in componentDidMount in the db, but maybe using a ref is basically the way to do that?

lilactown15:05:00

something like:

[:div {:ref #(trigger-event-with-new-dom-obj %)}]

lilactown15:05:09

yeah a ref is better to use for that

lilactown15:05:47

I’m guessing you’re doing something like assigning them an ID and querying the DOM based on that?

bfay15:05:24

Yeah, so each of the divs I'm talking about is a node in a graph. You can drag the nodes around the screen and connect them and stuff. Each node has an id, and I'm storing some state in the db map under [:nodes node-id]

bfay15:05:33

So I have a subscription that uses that state, and the node components subscribe to it

bfay16:05:04

So... I could maybe store the dom-node in the db this way, and write subscriptions that reference it? Something like:

[:div {:ref #(dispatch [:store-my-component-in-the-dom component-id %)}]
...
(reg-sub 
  :my-component-offset-width
  (fn [my-component-id]
  ;; ... somehow get the dom-node out of the db
  (.-offsetWidth the-dom-node)))

bfay16:05:21

I guess I'll have to play around with it

lilactown16:05:21

it depends on when you need to use the dom reference