New Todo web app in js_of_ocaml

Hi All,

I just finished a rewrite (up-to-date??) of Todo web app. It uses js_of_ocaml, react(opam react), reactiveData, dune, ocaml 4.10.0 and good old stdlib. Mostly, just good old standard tools that comes with ocaml and js_of_ocaml compiler.

One aspect of the codebase is that I have tried to incorporate component based implementation rather than the strict MVC implementation.

I am announcing it here in the hope that those new to ocaml, js_of_ocaml and in doing web app in ocaml can perhaps use it as another specimen to their learning journey.

repo: https://github.com/bikallem/jsoo_todomvc
running demo: https://bikallem.github.io/jsoo_todomvc/

Enjoy. :slight_smile:

12 Likes

Just wanted to mention that your project builds and works fine with ocaml 4.09.0.

1 Like

Cool!

Which dependency is the inline HTML coming from? Specifically:

That looks like tyxml. More specifically the js_of_ocaml-tyxml library in this instance.

Correct. It is tyxml - specifically this one index (js_of_ocaml-tyxml.index).
and
GitHub - ocsigen/tyxml: Build valid HTML and SVG documents

I’m wondering what benefits brings the use of polymorphic variants vs ordinary sum type for the action type. Any insight will be appreciated.

Mostly to get around circular dependency. i.e. I define action type in jsoo_todomvc.ml which is the topmost/root module, i.e. not other modules depend on it but it itself depends on other modules such as Footer, Newtodo and so on. That type (action) is used in other components/modules such as new_todo.ml and footer.ml which makes it circular, i.e. module Jsoo_todomvc depends on New_todo and New_todomvc depends on Jsoo_todomvc which is not allowed in ocaml - as it shouldn’t. Using polymorphic variant breaks this circular dependency. Perhaps this wasn’t the intended purpose of polymorphic variants but it is quite effective in this scenario.

You can get around circular modular dependency via recursive modules but in this instance it felt bit heavy since using recursive module you have to restructure your source code in a certain way,i.e define your dependent modules in the same file.

Another alternative to get around this circular dependency issue is to define the type in std.ml so you can use normal variant. However, I went ahead with polymorphic variant in this instance as they are quite flexible as compared to normal variants.

2 Likes

I would suggest that the action signal is better represented as an event stream. In that case the dispatch function simplifies to dispatch : action -> unit, instead of dispatch : action option -> unit.

I did consider that. I was wrestling with 2 alternatives,

  1. whether to create an action that did nothing - perhaps unimaginatively called `None. Which I can then subsequently use as a default value to create the action signal instance in line https://github.com/bikallem/jsoo_todomvc/blob/master/src/jsoo_todomvc.ml#L94.
  2. Or use the concept of nothing already well defined, understood and used in ocaml, i.e. 'a option type - specifically None - and use the functions defined over it - in Option module. https://github.com/bikallem/jsoo_todomvc/blob/master/src/jsoo_todomvc.ml#L100

In the end I went with (2) because 1 is more work :slight_smile: but also because it seemed semantically more correct and cogent for action type to not have None action.

All actions dispatched in the app do something. The only time the action signal is None is at the very beginning when you create the signal. But with an event stream, initially there is no event:

  let action_s, dispatch = React.S.create None in

  let action_e, dispatch = React.E.create () in

Why do you need an action that does nothing?

That would change the type of dispatch from action option -> unit to unit -> unit - which makes our dispatch function not very useful.

[1] https://erratique.ch/software/react/doc/React.S.html

Hmm, not really. You are looking at the docs for signal, not event. See https://erratique.ch/software/react/doc/React.E.html

val create : unit -> 'a React.event * (?step:React.step -> 'a -> unit)

The dispatch function will be 'a -> unit, in this case action -> unit.

1 Like

Ah, I see what you mean now. I haven’t managed to fully grok React.E yet.

I will give that a try. Thanks. :+1:

@borisd Indeed using React.E.create simplifies the dispatch function. Thanks. It is done now. https://github.com/bikallem/jsoo_todomvc/blob/master/src/jsoo_todomvc.ml#L21. :+1:

2 Likes