Bonsai, enum, executing all paths

This is one aspect of Bonsai I still do not understand.

        Bonsai.enum
          (module T)
          ~match_:tab
          ~with_:(function
            | A ->
              let%arr change_tab = change_tab in
              Vdom.Node.button
                ~attrs:[ Vdom.Attr.on_click (fun _ -> change_tab T.C) ]
                [ Vdom.Node.text "jump to c" ]
            | B -> Bonsai.const (Vdom.Node.text "why are you even here")
            | C -> Bonsai.const (Vdom.Node.text "hello!")))

In particular, my current understanding (no idea if this is correct) is that Bonsai somehow is ‘executing all branches’ and to prevent that, we need something like Bonsai.enum to only bother updating the visible tab.

Is the above intuition correct? If so, if there a write up somewhere of the mechanics of what is going on here (especially with respect to “bonsai executing all branches”) ?

Thanks!

Let’s say we have a dashboard with several pages. Each page is a large and complicated computation, with a lot of data processing and a lot of produced Vdom.

The simple way to display one of the pages at a time would be something like:

let router path =
    let%sub page1 = page1 in
    let%sub page2 = page2 in
    let%sub page3 = page3 in
    let%sub not_found = not_found in
    let%arr page1 = page1 and page2 = page2 and page3 = page3 and not_found = not_found and path = path in
    match path with
      | "/" -> page1
      | "/2" -> page2
      | "/third" -> page3
      | _ -> not_found

This component will return the Vdom of one of the pages, as desired. However, it will compute the view for ALL of the pages. Bonsai’s enum function (and match%sub) allow you to only compute the path that is shown.

On a side note, Bonsai has a routing library so you don’t need to reinvent that from scratch.

Thanks!

I have a few followup questions.

Does this happen at the let%sub : 'a Computation.t -> 'a Value.t or the let%arr : 'a Value.t -> 'a line ?

  1. So Bonsai.enum is kind of like a ‘match’ right? Is there a Bonsai.### that is like an ‘if’ ? I’m looking for something that does:

bool Value.t -> true_: Vdom.Node.t Value.t -> false: Vdom.Node.t Value.t -> Vdom.Node.t Computation.t (or something like that)

I haven’t worked with the internals enough to answer with 100% confidence. That being said, I’m pretty sure that let%sub calls “create” a new copy of the incrementally computed Value.t (and are likely responsible for the cost of computing and caching it), and let%arr just accesses the precomputed value.

I highly recommend reading https://github.com/janestreet/bonsai/blob/master/docs/blogs/letsub.mdx

If I’m remembering my Bonsai history correctly, there used to be a Bonsai.if_, which got deprecated in favor of the more general match%sub. You could just pattern match on true/false.

What were your main take aways from that blog post? Mine were:

  1. Recomputing things is bad.

  2. Think of 'a Bonsai.Computation.t as caching / memoization / only computing it once.

  3. let%arr produces Computation.t ; let%map produces Value.t. Value.t recomputes. let%map bad.

Were there any other points to extract ?

  1. Instead of “recomputing”, I’d say “redundant recomputation”.
  2. With Bonsai’s current API, I think of Computation.t as a “blueprint” for an incremental computation, and of Value.t as the actual incremental computation. let%subing a Computation.t multiple times will “create” multiple independent copies from the same blueprint.
  3. Value.t isn’t bad, and let%arring multiple times from the same Value.t is the mechanism for sharing work. Iirc, mapping on Value.t isn’t the issue, mapping on Computation.t is. But I’m not really sure, and would have to test that.

W.r.t. takeaways, I think that working through the simplified arithmetic DSL helped me understand what let%sub and let%arr actually do, and get an idea of what the implementation might be.

This is not a rhetorical question – I genuinely am confused about this. Is it the let%sub or the creation of the 'a Computation.t that creates the subgraph/node ?

In particular, consider :

let x: 'a Computation.t = ...
let%sub x1 = x in
let%sub x2 = x in
let%sub x3 = x in

Are “independent copies” being created here? If so, of what ?

To expand on this confusion a bit, this notion of “independent copy” and “caching / avoid recomputing” seem conflicting to me in the following way:

If we have an independent copy of something, say a subgraph, it seems, almost by definition, the computation is being dupliicated.