M s.t. 'a Value.t M.t == 'a Computation.t?

In the context of Bonsai, is there some M where

'a Value.t M.t == 'a Computation.t ?

I’m wondering if these is some module that captures the “gap” between Value.t and Computation.t

Not sure what you’re trying to do here tbh. There’s a Bonsai.read : 'a Value.t -> 'a Computation.t function?

I’m definitely running into an issue; I’m not sure how to formulate a question. It’s not a compile error. It’s more of an “architecture issue”; I feel like I’m trying to cover a 10x10 floor with a 8x12 rug.

It brings me back to How to Compose Janestreet/Bonsai components?

Could you add a section on composition to https://github.com/janestreet/bonsai/tree/master/docs/guide ? :slight_smile:

Either I am missing something obvious or this is something non-trivial to pickup from the examples since the “architecture” often gets buried by the “low level technical details”

Yeah, I get that. Figuring out how to use Value.t and Computation.t feels like solving a puzzle sometimes. Good news is, things might get a lot simpler. There’s a (very WIP) proposal that would use 'a Bonsai.t with let%map instead of the current Value/Computation and sub/arr syntax. You can see the proposed syntax at GitHub - TyOverby/composition-comparison, in the leftmost column.

For now though, here’s a simple “algorithm” to think about it:

  • if you want to use one of several Computation.ts, use match%sub
  • if you want to display a dynamic number of the same Computation.t, use Bonsai.assoc or an associated util
  • for other components (generally simpler):
    • if you want to use any Computation.ts, use let%sub to instantiate them into Value.ts
    • Use let%arr to construct your output from all the Value.ts

You should try not to think of composition in terms of 'model/'action, but of incremental computations producing some 'a. And at that point, it’s pretty much just a monad without bind, which uses some unusual combinators (arr/sub) to enable parallel composition.

I think that the best way to learn composition of Bonsai components is to practice doing it. I’m on vacation at the moment; before I left, I finished v1 of my tutorial for snake in Bonsai, which teaches how to design and compose stateful Bonsai components. I would recommend trying it out, and would appreciate any feedback.

I think here is my problem: There is exceptionally good documentation for how to create bonsai components with Model / Action / StateMachine. However, I’ve found almost no documentation explaining the techniques for creating non StateMachine components. It seems they are mentioned here & there in passing, but there is nothing like a central “cookbook” listing all the standard ways of composing/combining components.

I think I’m understanding what you’re getting stuck on, thanks for bearing with me.

Instead of the word “component”, I would think “computation”. One of Bonsai’s big strengths is that computations which return Vdom (Vdom.Node.t Computation.t) are treated pretty much the same as computations that return anything else (a string, a function, a record, etc).

So, what can a computation be?

  • The simplest is a static constant. For example, Bonsai.const "hello world" gives you a string Computation.t. This isn’t used a ton; if you’re building a constant, static site, it’d be much simpler to just write the HTML.
  • There’s a bunch of Bonsai “primitives”, mostly to do with creating, resetting, and sharing state. Bonsai.state is the simplest, but a lot of the Bonsai.* functions fall into this category. Its worth noting that the output of these functions is some 'a Computation.t, so they are composed and combined just like any other computation.
  • There’s Var.ts, which are essentially mutable values that you can “listen” to as Value.ts. These are generally used to pass in info from “outside” of your Bonsai application. For example, receiving RPC call results, or keeping track of the current URL.
  • And the most common category, which is computations that are defined as a function from other computations (and constants). These are created by using let%sub to instantiate “input” computations into incremental values, and let%arr to define the new computation in terms of its Value.t inputs. For example:
let wrap_content inner =
  let%arr inner = inner in
  Vdom.(Node.div [
    Node.p [ Node.text "label"];

We build a new component from an existing one and some constant Vdom.

What “type” of component are you trying to create?

Let’s start with blender. Then let’s remove the 3D rendering part. What we are left with is a bunch of 2d windows, panels, forms, trees, etc …

I think part of the struggle is this.

  1. Most are familiar with model-view-controller

  2. Most are familiar with React’s data → Vdom.t

Because of the above, the Model.t, Action.t, apply: Model.t → Action.t → Model.t are very familiar.

So now we have this issue of how do we build bigger things from smaller things.

  1. If the answer is: bigger state machines from smaller state machines – the I’m missing something obvious, see the struggle in How to Compose Janestreet/Bonsai components?

  2. If instead, the answer is not: bigger state machines from smaller state machines, it seems like we are deviating off the path of model-view-controller or react’s data → vdom.t ; it’s a bit unfortunate that just as we leave the familiar analogies, the documentation / guide ends. :slight_smile:

That said, it is entirely possible I am missing something very obvious, as it appears no one else is struggling with this / bonsai.

Kind of, but as a user of the framework, this is done for you.

Have you tried the tutorial I’ve been recommending?

If you’d like, I’d be happy to hop on a call and work through some examples / what you’re stuck on.

Your todo_list / snake tutorial? Still have not worked through it. :slight_smile:

Thanks, I might take you up on this offer down the line. At the moment, my code is a mess and not ready to share.