Is there any clojure(script) implementation for reactive values?

While (functional) reactive programming or functional reactive stream is great, it ends up increasing coupling between inter-dependent widgets because functional reactive streams don’t incur side effects.

Referential transparency of pure transformation functions for functional reactive streams forces programmers to explicitly define relationship between interdependent widgets in one place.

Reactive values is a non-functional approach to reactive programming that eliminates coupling between inter-dependent GUI widgets through use of mutable references. Mutable references eliminate coupling between inter-dependent widgets.

Decreasing coupling is allegedly an important goal in programming.

Reactive Programming using Reactive Values – Keera Studios introduces reactive values. Haskell’s keera hails framework offers reactive values for haskell programmers.

Is there any clojure(script) implementation of reactive values?

2 Likes

Can you provide additional sources for this? I think that blog post is pretty confused.

Missionary supports Clojure atoms as reactive inputs, and then uses m/watch to derive a missionary flow from the atom.

Another insight is that, once an expression is lifted into an incremental evaluation context whose execution is managed by an effect system, the effect system abstraction gives us surgical precision as to exactly which pieces of the computation need to be recomputed in response to some subset of inputs changing. (This falls out of the event propagation network / DAG which is how effect systems are implemented.) The insight is: once you have surgical precision as to when each point in the dag is recomputed, and a controlled lifecycle for each point, you can attach side effects to that lifecycle and know that the execution of those effects is tightly controlled and supervised and with the same lifecycle as the DAG. In Haskell this would be modeled as a Kleisli arrow, which lets you attach a monadic effect to any edge in the DAG. (Arrow is the typeclass that gives you DAGs directed graphs, in Haskell (->) is an instance of the Arrow typeclass. Reactive functions a la FRP are also instances of Arrow.)

RX, Dataflow, and Effect system can be used interchangeably here, I think they are all the same thing. The unifying abstractions are arrow, monad and comonand.

References

Do you think reactive values are similar to or the same as reactive streams implemented by ReactiveX or bacon.js?

Almost all ClojureScript UI frameworks are reactive in their design (not functional reactive, just reactive).

I’d say FRP is not as common in ClojureScript, though those also exist, but they haven’t yet made it as mainstream in the Clojure community I’d say.

What you need to realize though is that each such lib has its own twist and flavors for how they do RP or FRP. So when you say does Clojure has some “ReactiveValues” as in some niche lib from a Haskell studio called Keera, well, probably it doesn’t have a copy/past port of it exactly as it is designed with the same exact abstractions and functions and interfaces, but I’m sure it has a lot of things within a similar ball-park, so the question is what are you looking for exactly?

For example, in Reagent you have a kind of atom (colloquially called a ratom) that tracks its component dependencies, and when its value changes all components react to the change in value by re-rendering themselves with the updated value. You can then create derived ratoms from others, so another atom that is a computation which include other ratoms in it, and if anything they depend on changes, they too will get recomputed and their value will become the updated result of their computation, causing components they depend on to also re-render, etc.

Edit: Special Mention to Javelin: GitHub - hoplon/javelin: Spreadsheet-like dataflow programming in ClojureScript. because to me its the most direct implementation of the idea of RP as described by the wikipedia summary for it: Reactive programming - Wikipedia

The summary says:

For example, in an imperative programming setting, a := b + c would mean that a is being assigned the result of b + c in the instant the expression is evaluated, and later, the values of b and c can be changed with no effect on the value of a. On the other hand, in reactive programming, the value of a is automatically updated whenever the values of b or c change, without the program having to explicit re-execute the statement a := b + c to determine the presently assigned value of a.

And here is the example it shows:

var b = 1
var c = 2
var a = b + c
b = 10
console.log(a) // 3 (not 12 because "=" is not a reactive assignment operator)

// now imagine you have a special operator "$=" that changes the value of a variable (executes code on the right side of the operator and assigns result to left side variable) not only when explicitly initialized, but also when referenced variables (on the right side of the operator) are changed
var b = 1
var c = 2
var a $= b + c
b = 10
console.log(a) // 12

With Javelin this would be represented as:

(defc b 1)
(defc c 2)
(defc= a (+ b c))
(reset! b 10)
(println @a) ;; 12

It doesn’t get any closer than that :smiley: its line by line the same code in Lisp syntax.

That sounds like reactive values, but reactive values aren’t tied to GUI widgets. I want a generalized library that can be used anywhere.

I updated my above answer to give a shout-out to Javelin, that’s gonna do what you want. Its a generic RP library for Clojure. Its actually used in the Hoplon web framework to build UIs as well, but can be used for anything.

Edit: Also note that existing Clojure atoms, vars, refs and agents can also have “watchers” registered to react to their changes. They’re not exactly RP, but you can still use it to subscribe to changes to them and do stuff when they change, see: add-watch - clojure.core | ClojureDocs - Community-Powered Clojure Documentation and Examples

I am not yet familiar with javelin, but it seems similar to reactive values.
It seems javelin is dying along with hoplon. Too bad.

  • Do re-frame and fulcro have something like ratom, missionary, or javelin?
  • Are re-frame and fulcro compatible with ratom, missionary, or javelin?

I mean, Javelin seems pretty much done to me, there’s not much you’d need added to it. I’m not sure what “dying” implies, but if it works why can’t you use it?

It’s also all open-source, so if you decide to use it and you find a bug just fix it yourself, or if you need more features just fork and add them.

I don’t know about Fulcro, but re-frame is based on Reagent, and so it’s also makes use it its ratoms, I believe that’s what it may use for its subscription machinery. That said, I’d say in some ways re-frame might be more event driven with an event loop.

It depends what you mean by “compatible”? Nothing stops you from depending on all of them in your app, but I don’t know if it’ll be helpful to use them all together.

To me, dying means there has been no commit for more than a year and pull requests have not been merged for more than a year. I would have to fork it if I wanted to keep using it.

Compatible means it’s not difficult to hook up re-frame or fulcro with reactive programming constructs such as ratom, RxJS, missionary, or javelin. One has to consider the possibility that it’s impractically difficult to hook up reactive programming constructs with UI frameworks.

I’m guessing you’re new to Clojure? Ton of Clojure libs would qualify as dead by that definition, yet work well and are still used in production without issues.

Ask yourself, why would you need to fork it if it works?

A better metric in my opinion would be to look into what the library does and depend on? If it uses anything external to Clojure, like if it wraps some APIs of the OS or some other web service. That’s when you’ll want to make sure it’s well maintained, so it keeps up with updates from the OS and the APIs it wraps. And you’d want to look into if it has all the features you’re looking for or not. And you’d want to check if it seems mostly bug free or not. Finally you’d want to check how big it is, if it’s under 3kloc you can probably easily contribute to it if you needeed too, if it goes beyond probably would get harder.

A library that depends on very little beyond Clojure itself, doesn’t interact with the OS, hardware, external APIs, or external formats, is mostly a language extension that once it works, would have no reason to stop working. And one that’s only around 1kloc or less would be easy to patch yourself.

To me Javelin qualifies as that (it’s only 1kloc, depends on very little beyond Clojure/Script, has no dependency on OS, hardware or APIs, is battle tested from having been used in prod for a long time, seems to have all the features I can think are needed of a RP library).

I would say that yes it is impractical. All these UI framework are already bringing their own opinion on managing state, transitions and renders, most of them by promoting a kind of reactive model of their own. For you to go in and try to change that to your own reactive model would probably be impractical and also defeats the point of using a framework. If you have your own opinion of how to do things, frameworks are not what you want.

2 Likes

Do you actually know that it is impractical? Or, do you assume it is impractical?

  • “ratom” is reagent.core/atom
  • Reframe depends on (is built in terms of) reagent
  • Reagent depends on React, but bypasses the React evaluation model, with it’s own digest loop that calls React’s forceUpdate API. It’s important to understand that the Reagent model is not FRP. There is no DAG, it’s not functional programming in any way, it is based on side effects of the bad kind. This is manifest in lots of “FRP glitches” and other gotchas where Reagent produces inconsistent or buggy states that app programmers learn how to work around.

ratom sounds similar to reactive values. Ivan Perez who researched in (functional) reactive programming devised reactive values in order to increase decoupling between interdependent GUI widgets.

He thinks reactive value is better for decoupling interactive widgets and functional reactive streams are better for animations, simulations, or any other kind of non-interactive streams.

Maybe, reactive values are better than ratom.

Decoupling makes it easier to scale a codebase.

fun fact: javelin can be used with Rum’s reactive mixin.

2 Likes
  • What are side effects of the bad kind?
  • Any better alternative to ratom?

Although I didn’t post it, I’m going to take a stab anyway as I think it is an interesting question.

In Clojure/Script there are essentially two ways of mutating something. One is to use one of the built-in reference types such as atom, ref, var, agent. Only atom is available in ClojureScript, and is in fact, at least as far as I know, the favoured way to perform mutations in both languages. The essential idea here is that you have a single top-level “object” which whose value you mutate via reset! or swap!. Because you can add validators, you have some level of control over which values you can place in any given atom. As such this is said to be “managed mutability”. And of course you can also add and remove watches, functions which are called whenever the atom is updated successfully.

The second way is to use deftype (not defrecord) to create an object with one or more mutable fields, by marking each mutable field with host-specific metadata (e.g. ^:mutable in CLJS), for example:

(deftype Person [name ^:mutable age])

Now, if I create an instance of the above type, if I want to mutate its age property, I can do so using set!:

(def me (->Person "Outrovurt" 40))
(set! (.-age me) 45)

This is said to be unmanaged mutability, since you can set any field to any value due to there being no validation whatsoever, and you can’t watch these objects at all. This is what constitutes side effects of the bad kind.

Regarding Reagent, it is an excellent choice if all you want to do is to use React in your web projects. But beyond that, I don’t really think you can just take the ratom out of it and use it in any given context. As far as I know it is something which is very much tied to Reagent itself, and Reagent is very much tied to React, though I could be mistaken here.

I would certainly look into the options that others have suggested as one of those may be what you are looking for. Certainly Javelin appears to provide a more general solution than Reagent ratoms.