From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id 77DB27EE9C for ; Thu, 1 Dec 2016 23:18:45 +0100 (CET) Authentication-Results: mail2-smtp-roc.national.inria.fr; spf=None smtp.pra=yminsky@janestreet.com; spf=Pass smtp.mailfrom=yminsky@janestreet.com; spf=None smtp.helo=postmaster@mxout1.mail.janestreet.com Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of yminsky@janestreet.com) identity=pra; client-ip=38.105.200.78; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="yminsky@janestreet.com"; x-sender="yminsky@janestreet.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail2-smtp-roc.national.inria.fr: domain of yminsky@janestreet.com designates 38.105.200.78 as permitted sender) identity=mailfrom; client-ip=38.105.200.78; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="yminsky@janestreet.com"; x-sender="yminsky@janestreet.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@mxout1.mail.janestreet.com) identity=helo; client-ip=38.105.200.78; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="yminsky@janestreet.com"; x-sender="postmaster@mxout1.mail.janestreet.com"; x-conformance=sidf_compatible IronPort-PHdr: =?us-ascii?q?9a23=3ACSRQrxIiL3UDaXIu7NmcpTZWNBhigK39O0sv0rFi?= =?us-ascii?q?tYgUK/rxwZ3uMQTl6Ol3ixeRBMOAuqkC17Od4/ioGTRZp83e4DZaKN0EfiRGoP?= =?us-ascii?q?tVtjRoONSCB0z/IayiRA0BN+MGamVY+WqmO1NeAsf0ag6aiHSz6TkPBke3blIt?= =?us-ascii?q?dazdU7TfhMWv1u2054abI0AR3GL8MvtOK0CfqQzQsIE4m4p5IaZ5nhLNq3pOPe?= =?us-ascii?q?pMxHhjJXqXkgb96Mb295lmpXd+ofUkov9JS6L8N4E5S6dbHXxyImU04tbopDHB?= =?us-ascii?q?RA2C/WcGX2gK1BFPBl6Wv1nBQp7tv36i5aJG0y6AMJizFOhsVA=3D=3D?= X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: =?us-ascii?q?A0CbAACIoEBYfU7IaSZSCRoBAQEBAgEBA?= =?us-ascii?q?QEIAQEBARUBAQEBAgEBAQEIAQEBAYMNAQEBAQF3gQYHpEmWfSuFdwKCBQdDEAE?= =?us-ascii?q?BAQEBAQEBAQEBEgEBCRYJTYIzBAEVAQSCFgEBAQMBEhEEGQEBNwEECwsLDQICE?= =?us-ascii?q?RUCAiISAQUBHAYTIohDCAMLnnqBMj8yimlngWw9gwwBAQWILAEBAQEBAQEBAQE?= =?us-ascii?q?BAQEBAQEBAQEWCBJ5hTOEW4QiEVWCRYJdj3qKaYZLgxCHNYJCjXSNeoJIEx6BE?= =?us-ascii?q?zWBGBMOIxECgxcPHIF7VAEBh3KBTwEBAQ?= X-IPAS-Result: =?us-ascii?q?A0CbAACIoEBYfU7IaSZSCRoBAQEBAgEBAQEIAQEBARUBAQE?= =?us-ascii?q?BAgEBAQEIAQEBAYMNAQEBAQF3gQYHpEmWfSuFdwKCBQdDEAEBAQEBAQEBAQEBE?= =?us-ascii?q?gEBCRYJTYIzBAEVAQSCFgEBAQMBEhEEGQEBNwEECwsLDQICERUCAiISAQUBHAY?= =?us-ascii?q?TIohDCAMLnnqBMj8yimlngWw9gwwBAQWILAEBAQEBAQEBAQEBAQEBAQEBAQEWC?= =?us-ascii?q?BJ5hTOEW4QiEVWCRYJdj3qKaYZLgxCHNYJCjXSNeoJIEx6BEzWBGBMOIxECgxc?= =?us-ascii?q?PHIF7VAEBh3KBTwEBAQ?= X-IronPort-AV: E=Sophos;i="5.33,284,1477954800"; d="scan'208";a="247719641" Received: from mxout1.mail.janestreet.com ([38.105.200.78]) by mail2-smtp-roc.national.inria.fr with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 01 Dec 2016 23:18:44 +0100 Received: from tot-qpr-mailcore2.delacy.com ([172.27.56.106] helo=tot-qpr-mailcore2) by mxout1.mail.janestreet.com with esmtps (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.84_2) (envelope-from ) id 1cCZgh-0002NI-8J for caml-list@inria.fr; Thu, 01 Dec 2016 17:18:43 -0500 X-JS-Flow: external X-JS-Scanner-attachment: (ok) No attachments Received: by tot-qpr-mailcore2 with JS-mailcore (0.1) (envelope-from ) id BYQKHD-AAABTH-GB; 2016-12-01 17:18:43.194093-05:00 Received: from mail-ua0-f197.google.com ([209.85.217.197]) by mxgoog1.mail.janestreet.com with esmtps (UNKNOWN:AES128-GCM-SHA256:128) (Exim 4.72) (envelope-from ) id 1cCZgh-0001DP-3o for caml-list@inria.fr; Thu, 01 Dec 2016 17:18:43 -0500 Received: by mail-ua0-f197.google.com with SMTP id b35so246426519uaa.1 for ; Thu, 01 Dec 2016 14:18:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=janestreet.com; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=ElYtrJ2QMNzU4JbJQI1mnFFWQCtKgS38c21PYVy4wQQ=; b=HJTRk5FdwaZH7be1LnMYeO0UK1aY/yXDBDNjMK7Hb97QdmQz/Br+PY/mjmRIlM9g7R txCJ37ITGQWRWfKPntrWqZV3NxpikoeD4fjOa1DNhHE6rlD0ARUQIh9fS6OJ7Y+C08B0 xOKpaNqIavhl9xF990qINlyN5vBYsXfjDkcks= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=ElYtrJ2QMNzU4JbJQI1mnFFWQCtKgS38c21PYVy4wQQ=; b=ZuC1QffRGG+S/tvwZXVHFfO1q5S9En+DCExlhwQkSXWbIuYSglfZ2Vlo5FdSJeJ26O tp2Qke0gtoMAy47aRgIxBtQvJEHmYDwHeMwoF4dkHhGup5Od64p9IxfDYv9Ute+/ERth edTuRdApa5ON9rdJR1BKhEtfSFRiA4FTsTIfE0qznvIxYRWd+XCtS6RCnp8igbJO22eD J56xsMq11K6hAHAkDC0gDLTmo+z5FG+7oDERqjv5X78iiQRAC53j6JInQlEFMNEB6pzP Qwp1wHQZPlmE6FeWlPk9DXSdRYjE0BQ0EC8J/kCSZJw7ZgW70UHY3+maM4kWH83YO/+/ Hw/Q== X-Gm-Message-State: AKaTC012gi40NDDxx+GyxdtMjSDUMTSjI3WHG06B+KVA03Bb9VN78+8gKhsU5lVcj5GTEKDhNdCEk7ObEKH+HF36sl5Q2sppzNPIO+C4IAeReknjEbhTe7aEGVjRa5RXRyCPn5583NI+tZunQ6ws X-Received: by 10.176.80.154 with SMTP id c26mr30560857uaa.136.1480630722594; Thu, 01 Dec 2016 14:18:42 -0800 (PST) X-Received: by 10.176.80.154 with SMTP id c26mr30560828uaa.136.1480630722248; Thu, 01 Dec 2016 14:18:42 -0800 (PST) MIME-Version: 1.0 Received: by 10.31.229.193 with HTTP; Thu, 1 Dec 2016 14:18:21 -0800 (PST) In-Reply-To: <0f6d69bb-a458-5876-3c60-a29befab02d1@lexifi.com> References: <96757896-e79c-f940-fc3a-090fc1419df2@lexifi.com> <0f6d69bb-a458-5876-3c60-a29befab02d1@lexifi.com> From:Yaron Minsky Date: Thu, 1 Dec 2016 17:18:21 -0500 Message-ID: To:Alain Frisch Cc:ocsigen@inria.fr, OCaml Mailing List Content-Type: text/plain; charset=UTF-8 X-JS-Processed-by: mailcore Subject: Re: [Caml-list] Announce: ocaml-vdom (pre-release) On Thu, Dec 1, 2016 at 4:32 AM, Alain Frisch 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