caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Yaron Minsky <yminsky@janestreet.com>
To: Alain Frisch <alain.frisch@lexifi.com>
Cc: ocsigen@inria.fr, OCaml Mailing List <caml-list@inria.fr>
Subject: Re: [Caml-list] Announce: ocaml-vdom (pre-release)
Date: Thu, 1 Dec 2016 17:18:21 -0500	[thread overview]
Message-ID: <CACLX4jRrcsENNA7cyAig-MU_TQyTSu8NVZ-0qchEsV7-Q21Hug@mail.gmail.com> (raw)
In-Reply-To: <0f6d69bb-a458-5876-3c60-a29befab02d1@lexifi.com>

On Thu, Dec 1, 2016 at 4:32 AM, Alain Frisch <alain.frisch@lexifi.com> wrote:
> Hi Yaron,
>
> On 30/11/2016 20:22, Yaron Minsky wrote:
>>
>> I'm curious if you have any story for making the recomputation of the
>> virtual-dom itself more efficient.
>
>> ...
>>
>> That said, for small UIs, this kind of incrementality is less
>> important, so whether this is worth doing may depend on your
>> applications.
>
>
> Our story w.r.t. updating the vdom itself is the same as Elm, I believe:
>
> 1. In the vast majority of cases, the mapping from the "state" to the vdom
> is very quick and it is ok to recompute the full vdom on every state change.
>
> An argument often made in the vdom space is that a UI usable by human-beings
> cannot possibly have a very big DOM (moreover, even recent browsers such as
> the latest Edge or Firefox struggle with tables starting at a few thousands
> rows), and that computing the full vdom "cannot be that costly".  I don't
> want to enter such discussion here, but in our own use cases, we indeed try
> to avoid "overly big UIs" and we don't have strong performance requirements
> such as continuous streams of updates pushed by the server; if it takes 10ms
> to react on a user UI event, it is perfectly fine for our case.  My
> intuition is that most applications would be a similar situation, but I
> appreciate that you have very different kinds of UIs and performance
> constraints that require a more incremental approach.

I do think for even moderate size views of data (big tables, graphs,
etc.) this argument stops working pretty quickly, but I agree there is
a large scope of applications for which this is true.

It's worth noting that we use Incremental to pull off what we've
called "partial rendering", which is to render a table with, say, 10k
rows, but only actually rendering the 60 or so that are actually in
view. This makes what would otherwise be rather sluggish applications
quite zippy.

> 2. The library provides:
>
> val memo: ?key:string -> ('a -> 'msg vdom) -> 'a -> 'msg vdom
> (** Apply the function to generate a VDOM tree only if the function
>     or its argument have changed (physically) from the previous
>     synchronization. *)
>
> (similar to Elm's Lazy:
> http://package.elm-lang.org/packages/elm-lang/html/2.0.0/Html-Lazy )
>
> This is typically used when embedding the view of a "sub-component" derived
> from only part of the our current state.  If we can ensure this sub-part
> remains the same, the previous vdom (at the same "site") is reused, which
> skips both the vdom generation and the vdom diffing. Typically, the
> generation function would be a toplevel declaration (so it will always be
> the same, physically) and we arrange to avoid touching the part of the
> global state on which it depends.
>
> Of course, it is also possible to use any other mechanism (such a
> memoization table) to reuse previously computed vdoms.

One downside of the Elm style approach is that physical equality is
brittle, e.g., if you end up constructing something that's logically
but not physically equal, you can end up recomputing a lot more than
you might have expected. I do think making it possible to add a custom
equality function makes this a bit better, and is an easy change to
make.

>> Another thought: you might want to consider the design we used for our
>> wrapper on Matt Esch's virtual-dom library. In particular, in our
>> design we don't need a type-parameter for vdom nodes determining the
>> type of a message, and we don't need the corresponding map
>> functions. Instead, we use open types and a registration and dispatch
>> mechanism for values thus injected.
>
> Thanks for the hint.
>
> Do you have pointers to code examples using the "inject" function?  It seems
> to me that you will need to apply to it produce any "message" in the view
> function.
>
> In our library, you can write directly:
>
>      input "+" ~a:[value "+"; type_button; onclick `Plus]
>
> With the "inject" approach, do you need to write it like:
>
>      input "+" ~a:[value "+"; type_button; onclick (inject `Plus)]
>
> ?

Here's an example:

https://github.com/janestreet/incr_dom/blob/master/example/incr_decr/counters.ml

And the tradeoffs you imply is indeed there. I find the code without
the type parameter easier to use and think about, but I agree that the
"lightness" argument isn't 100% clear.

Another advantage of the Event approach is that we have combinators
like Event.Many that let you deliver a list of events together, and
primitive events like Stop_propagation and Viewport_changed, which are
always available, independent of the concrete event type. We've found
this to be quite useful in separating out the pure and imperative
parts of the application.

> If so, this does not seem strictly lighter than using the "map" function
> occasionally.  Moreover this seems to open the door to possible problems if
> a vdom fragment producing some kinds of messages is injected in a "host
> application" that cannot process these messages.  It also means that one
> needs to take the identity of the "inject" function itself into account in
> order to memoize vdom-generation functions.

The issue about the identity of the inject function is real, and the
use of Incremental helps us here, making it easy to preserve physical
identity. Also, Incremental allows you to be freer in your use of
functional programming idioms, letting you pass closures around to the
various render functions without destroying physical equality.

As far as I understand it, the Elm-style approach tends to require a
fairly first-order style in order to make memoization effective.

That said, using Incremental is a pretty heavy hammer to solve this
problem, so if you don't care about incremental computation more
generally, it's not clear it's worth the trouble.

> Basically, we need "map" on "component boundaries", and even not always,
> since components can take ad hoc injection functions to wrap their messages
> into their "host" own message type (as the SelectionList example in
> https://github.com/LexiFi/ocaml-vdom/blob/master/examples/vdom_ui.mli ).
>
> The library does use extensible types (with a registration/dispatch
> mechanism) in two other places, though:
>
>   - "Commands" (encapsulation of side-effectul operations).  The set
>     of "command constructors" is small and has a global nature
>     (e.g. "AJAX query", "timer", etc), while the type of "messages"
>     is specific to each application and component.

Nice. This is part of what we're doing in our Event type.

>   - "Custom nodes" in the VDOM which allow plugging "native"
>     components (again, the set of such possible components is more
>     "fixed" than messages).

Interesting. I'll take a look.

y

  reply	other threads:[~2016-12-01 22:18 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-11-30 16:52 Alain Frisch
2016-11-30 19:22 ` Yaron Minsky
2016-12-01  9:32   ` Alain Frisch
2016-12-01 22:18     ` Yaron Minsky [this message]
2016-11-30 22:46 ` Martin DeMello
2016-12-01  9:56   ` Alain Frisch
     [not found] ` <CAG+nEjzO1qFfxHSMqueiKcTJyJYnREmvXhzGR7H+noBmV2oUKw@mail.gmail.com>
2016-12-02 13:41   ` Alain Frisch
2016-12-02 16:59     ` Vincent Balat
2016-12-02 18:18       ` Alain Frisch
2016-12-02 22:31     ` Yaron Minsky
2016-12-10 13:34       ` SP
     [not found]     ` <5db7c03d-bec8-8285-b458-82e681842dbb@zoho.com>
2016-12-05 15:55       ` Ashish Agarwal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CACLX4jRrcsENNA7cyAig-MU_TQyTSu8NVZ-0qchEsV7-Q21Hug@mail.gmail.com \
    --to=yminsky@janestreet.com \
    --cc=alain.frisch@lexifi.com \
    --cc=caml-list@inria.fr \
    --cc=ocsigen@inria.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).