[ANN] Brr 0.0.1, a toolkit for programming browsers

What are the differences with the default bindings provided in js_of_ocaml to the browser APIs (e.g., js.mli, dom.mli, etc.)?

I’m not sure exactly what you are asking but:

  1. If you are asking about the way API are exposed: brr does not type JavaScript’s objects as phantom types. It simply relies on OCaml’s abstract data types and plain functions. More about this can be found in brr’s FFI manual and FFI cookbook.
  2. If you are asking about binding coverage, you should be able to get a sense of what is bound in brr here.

Regarding 2. brr's coverage of more recent browser APIs is broader and more consistent than in js_of_ocaml – Promise support, Fetch, Service workers, Media capture APIs, WebGL2, Webcrypto, WebAudio, etc. Conversly older APIs supported in js_of_ocaml may not supported in brr (e.g. XMLHTTPRequest). Besides brr's coverage of some of the DOM element-specific interfaces may be shallower than in js_of_ocaml. There is however good coverage for the HTMLMediaElement, HTMLCanvasElement, HTMLFormElement and HTMLInputElement interfaces. For the rest the attribute and property API and the occasional trivial FFI method binding should be able to get you a long way.

3 Likes

In the doc at https://erratique.ch/software/brr/doc/Brr_note_kit/Ui/index.html I can see a nice application screenshot. Is there any chance the code of this app is available somewhere, as a way to learn how to code web apps using Brr_note_kit.

1 Like

Unfortunately not, I made that for a client and it is closed source.

As I said in the original announce don’t pay too much attention to the Note based stuff for now, it needs a few more design iteration.

But on due time I might do a similar drawing app project as sample code, this used Vg for rendering and Note for input and the way everything composed and decomposed was quite encouraging.

2 Likes

That would be great! I’m a FRP newbie. I used to code lablgtk+cairo desktop app, but I’m curious how similar things could be better encoded with FRP using Brr and Canvas.

One thing I forgot, is that there is a todomvc example in the repo, see todomvc.{html,ml} in this directory.

It doesn’t use the UI toolkit you mentioned, just the basic reactive DOM support provided by Brr_note and Brr_note_kit. But you can see how quickly you get reusable and composable components like bool_editor and string_editor.

The program structure in that example is quite similar to the one I had in the drawing app. You define a purely functional, non reactive data model, actions over the data model, create small UI fragments that renders parts of your data model and generate actions events for it, gradually glue them together using note combinators and finally define a fixed point signal that holds the data model as massaged by the actions events of your UI (as mentioned I’d like to replace fix points by direct let rec and a lazy infinitesimal delay combinator).

There are a few pitfalls like you should avoid retaining parts of your data model in the UI otherwise you could get outdated data come back in your model (makes for very fun and spooky bugs though). Identity in the data model is also a bit tricky, it seems in todomvc I used ==. That didn’t work in the drawing app where my surfaces had properties that could be updated but they could also be linked toghether (that window belongs to that wall etc.) so I needed stable identifiers for which I introduced a little abstraction to identify values and define relations between them.

One thing I remember fondly when doing the drawing app is that I would still get the odd interaction glitches you get when coding direct mouse manipulation interactions (surface definition/selection/move/transform) however thanks to the ability to denotationally reason and act (left leaning E.select) on the simultaneity of events, they were easy to understand and fix in an explicit way (that is via a defining expression).

Also if you get into Note the denotational semantics notation is not yet explained there, refer to the one of react it’s the same.

3 Likes

How hard would it be to build on top of Brr_note something like an Elm Architecture-style toolkit? I know there’s a TEA-Bucklescript library, but I’d rather use something relying on dune/jsoo.

I’ve read somewhere else that you were a bit skeptical about the advantage of MVU (movel-view-update) over MVC, but I personnaly find the counter UI example in ELM at


far simpler than the corresponding one in Brr at

There’s no need for those E.select. The UI is IMHO more declarative in ELM.

I don’t know. I didn’t look into MVU too much, but to me it’s largely a remix of MVC – despite what its proponents try to tell you. Since we now live in an age of software adverstising it’s a bit hard to get frank assessments.

As far as I’m concerned the compositionality story of MVU doesn’t look great. Basically it enforces state machines on you, and composing state machines is a bit meh. In FRP state machines become signals (via S.accum) which are highly composable entities with fine granularity (and bonus point, a well defined denotational semantics for equational reasoning).

If you are looking for MVU I think you can simply jump on LexiFI’s vdom. But when I see how you get to compose two models in that paradigm, I’m not convinced.

That example could be rewritten (I didn’t write the examples in this repo) to be more like the ELM one in it’s declarations.

But I think the ELM example is also more rigid. You may not like that E.select on this toy example, but you may get to enjoy it you when you start composing larger systems from smaller components.

5 Likes

You might be interested in Bonsai! At some level, you can think of it as a library for building composable state machines. It uses Incremental as its engine for incrementalizing the computation of views, with a virtual-dom implementation underneath.

It’s the primary tool we use for building UIs inside of Jane Street.

In some ways, Bonsai is like Elm, but it has its own interesting ideas. Some of the concepts are borrowed from this paper:

though I won’t pretend to understand this paper myself!

Bonsai doesn’t yet have enough public-facing documentation, and really the bleeding edge version on github is considerably better and more usable than the one released into opam. But there’s at least one public-facing UI that’s built with it, if you want a real-world example.

4 Likes

Thx for the links!

The memtrace viewer example is pretty cool, but Bonsai looks far more complicated than ELM.
If you look at the counter example (the hello world of UI), here:

and you compare it to the one in ocaml-vdom (thx @dbuenzli for the link) at https://github.com/LexiFi/ocaml-vdom/blob/master/examples/counters/counters.ml

there’s a huge difference in simplicity.

BTW, is there something similar to ocaml-vdom but for programming desktop GUI? Something like a reactive lablgtk? With a MVU paradigm?

Hi Aryx, I wrote the Bonsai example that you linked, and it certainly isn’t the most concise, but that’s because it was built for a tutorial on building small components (one counter is a single component), how to use more advanced combinators (Bonsai.assoc), and how to move data from one component to another (the add_counter_component into the associated counters component.) I think it’s a great example of the power of structuring an UI as a DAG rather than a tree, but it definitely isn’t very small!

In the example, the comments that look like “CODE_EXCERPT_BEGIN” are actually preprocessor definitions that are used in the (honestly, kinda out of date) tutorial here. A bonsai app implementing the same functionality (but that wasn’t written for such a tutorial) would look more like this.

6 Likes

Much appreciated. Too often, tutorials are thought for people who already know modern web development with JS. I seem to remember you were also interested by Android apps as PWAs. Is it possible to use Brr in this setting? In that case, would you update your tutorial with this topic?

Brr is just a JavaScript FFI and bindings to browsers API and their documentation – and that’s what it intends to remain.

As a matter of fact you have the bindings to the APIs needed to make PWAs, I didn’t try do one so far but I might in the future; at which point more documentation might be generated.

1 Like

hey, I’ve been checking these different VDom implementations,

but what is the advantage of using Brr or JaneStreet’s Bonsai over something more popular like ReactJS? In terms of development experience ReactJS has a ton of documentation, benchmarks, cross-browser glue that handles edge cases, a dedicated team that works on it and a huge ecosystem and use in the industry -> you are not likely to be the first encountering bugs;

what problems do other vdom libraries like Brr or Bonsai solve? Are they faster or easier to use or something else entirely?

thanks

As mentioned in my previous message Brr is not a library to make user interfaces:

You could build one on top of it though.

Tried Brr recently in a miniature project and it was very pleasant to use. The most obvious win vs. standard Js_of_ocaml was that the basic types relating to JS interop were much more comprehensible (no super-long OO proxy signatures in editor hovers and compiler error messages!). The module factoring seems very thoughtfully done too.

I will look into using it more, particularly the HTML5 Canvas 2D functionality as an alternative to native code with SDL.

4 Likes

Also tried Brr recently to write applications and some bindings and I have to say I think it is brilliant! With Brr writing bindings is as easy as reading the Js API and converting to types. The cookbook has everything you need to get up and running :))

I’ve tried pure js_of_ocaml in the past and didn’t enjoy it a lot (even though I wanted to), but loved the prospect of writing OCaml for the web. And by that I mean “idiomatic” OCaml. Brr (for me) has made this possible. Thank you @dbuenzli!

14 Likes

As a heavy user of React (have a 10k line native app written purely in React), I’m personally unsure of what aspects you are hinting at here. Could you elaborate?

I have only found a few aspects of the API that I don’t like - but mostly I’ve found fine alternative ways around them. If people are interested, I’ll probably make a blogpost at some point where I go through the “style” I have settled on - e.g. (up-/down-)sampling, top-level signals/events throughout, reactive first-class modules (containing/depending-on events/signals) etc.

Concerning ‘correct’ usage of FRP - the primary hack I needed in my codebase is the concept of ‘previous’ values ('a option) where earlier reactive nodes can observe future nodes previous value (so a way of keeping code liftet into the FRP monad but having potentially whole-program recursion). But I limit my use of this to a few places, and only within certain first-class module ‘chunks’ in the program so I don’t break compositionality. In a game-loop or in Elms update function, one has the whole previous state available - which this emulates, while gaining all the time-based programming advantages of FRP

1 Like

This is basically written here but mainly the following points:

  1. Signal initialisation. Despite being “unsafe” people do use S.value to initialize other signals because they rightly don’t want to pay the cost of a S.bind to initialize a signal with the current value of another. But unless you understand what you are doing this will likely lead to seemingly random failures at runtime. Too subtle and brittle.

  2. Fix points. I think people have a hard time wrapping their head around
    S.fix and E.fix. Especially if you have multiple recursive definitions. What you want here is a lazy infinitesimal delay combinator and plain let rec (this is not done yet in Note). I suspect that your “primary hack” is about this, since of what I understand that’s exactly what fix points give you, namely the previous value of your event/signal an infinitesimal amount of time ago.

  3. Reactive outputs as effectful signals and events. I think that mixing effects with FRP is a bad idea.

    A lot of people came to me asking how they could control the order of side effects in update cycles. But this is antithetic to the synchrony hypothesis which states that everything that occurs in an update step is simultaneous. Besides a lot of people want do this do it in order to feedback the reactive graph which is forbidden.

    Most of these effects deal with ressources on which you want precise life-time management. This means you want control over the updates of these effectful signals and events. You can use S.stop. But this breaks equational reasoning and given the previous point. I think it’s better to avoid effects in the reactive network and formalize its outputs via another structure (loggers in Note).

2 Likes