From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail4-relais-sop.national.inria.fr (mail4-relais-sop.national.inria.fr [192.134.164.105]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q1EBq8sa020668 for ; Tue, 14 Feb 2012 12:52:08 +0100 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AtQCAKlJOk9KfVM2kGdsb2JhbABDrx91CCIBAQEBCQkNBxQEI4FyAQEBAwESAhMZARsdAQMMBgULDS4hAQERAQUBHAYTIodanEAKi3GCcIUmP4hzAgULiDmBeIEWBQEFAgIEAwMQBQgBAwcBAgMHCwMGBQcGAwMFBwgCEod0BI1lh02LEIMVPYQD X-IronPort-AV: E=Sophos;i="4.73,416,1325458800"; d="scan'208";a="131282014" Received: from mail-ee0-f54.google.com ([74.125.83.54]) by mail4-smtp-sop.national.inria.fr with ESMTP/TLS/RC4-SHA; 14 Feb 2012 12:52:03 +0100 Received: by eekb47 with SMTP id b47so2544406eek.27 for ; Tue, 14 Feb 2012 03:52:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:content-transfer-encoding; bh=BhN8FCdAvgoIykKmkEgyFRvbLzLQ1OSYAI0WvyzoZjc=; b=T/y8kZFP0l/+4BjEvqqjnR42fIX5knKbrg9dqyOUz3wKHmdV1xxmfxuDjW30In2lAb GUNvU4vzRFZi8IX8dmgusG+jusp4a8SrivTj14HAqCQnbB/3KHHwWQmLqRZGVGvERcxI EzuWH+8HOQOSukJ45JFl1YAa2Ky+uE1uL1RH8= MIME-Version: 1.0 Received: by 10.213.8.133 with SMTP id h5mr387361ebh.13.1329220322992; Tue, 14 Feb 2012 03:52:02 -0800 (PST) Received: by 10.213.6.195 with HTTP; Tue, 14 Feb 2012 03:52:02 -0800 (PST) In-Reply-To: References: <4EE9238E9F474D96925E5920255FD691@erratique.ch> Date: Tue, 14 Feb 2012 12:52:02 +0100 Message-ID: From: Adrien To: Philippe Veber Cc: =?ISO-8859-1?Q?Daniel_B=FCnzli?= , caml users Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-MIME-Autoconverted: from quoted-printable to 8bit by walapai.inria.fr id q1EBq8sa020668 Subject: Re: [Caml-list] Functional GUI programming: looking for good practices On 14/02/2012, Philippe Veber wrote: > Hi Daniel, > > 2012/2/14 Daniel Bünzli > >> Le mardi, 14 février 2012 à 11:02, Philippe Veber a écrit : >> >> > In other words, am I allowed to call a primitive in a lifted function? >> No. This is documented here [1]. > > Well I did read the paragraph, but thought the described limitation might > be about updating a signal from different threads. Of course it makes sense > that even in a single-threaded code, an update cycle should not be > interrupted by another if there are shared dependencies. May I nevertheless > humbly suggest to add the "not calling a primitive inside a lifted > function" rule more explicitely, as it is a tempting sin for a beginner? Quite often, doing so does not make a lot of sense. Suppose that you have a signal A. An external event triggers an update which will create A1. But during that update, you trigger another update which will create A2. Now, how does your signal change? Is A2 going to be the next value of A or is will it be A1? A simple (and probably not proper) implementation of FRP that I've done simply used A2 and then A1: the additional work that had been done for A2 was being dropped. Some implementations or approaches will probably solve that one way or another but that's not my point: when you do that, the code becomes non-obvious. >> One way to side-step the issue is to put these updates in a queue and >> execute them after the update cycle ended. >> > That seems a good work-around, to gather all side-effects in one place, > namely a (unit -> unit) Queue.t where to defer all calls to the primitives. > That do help, thanks ! > ph. I've had to use that until I changed the order of some operations in my code. I might have managed to break a bigger cycle into two smaller and independant cycles but I need to check, document, triple-check, ... what I've done. Anyway, I quickly benchmarked "GLib.Timeout.Add ~ms cb" from gtk/lablgtk2 which calls cb after at least ms milliseconds from the mainloop. Its latency was around 122µs for ms = 0 in my case (obviously, the program was otherwise idle). I think you could also use lwt here but I haven't done anything with it. Btw, I think it's worthwhile to mention some issues that can create such a need. In my case of a web browser, web pages have javascript and the javascript can create a new page and get a corresponding object. var popup = window.open (); // or something like that In webkit-gtk (and probably all other webkit ports), this triggers a GTK signal to which you can attach a callback which returns a WebView object. However, my code does not allow a webpage to create a new page: all it can do is request that one gets created by the Zipper which holds the pages. So the page changes its state and stores a request for a pop-up from the callback. The request is seen by the Zipper which creates a new page in the UI side of my code. Once the page is ready, it gets added to the Zipper and a message is sent to the parent page. All that has been done in the callback. It now works with my "MiniFRP" implementation but it definitely had the A/A1/A2 issue I mentionned above. If you put the updates in a queue and delay them, you're probably not going to solve this issue which turns out to be pretty simple. With the callback, you've started an update cycle and you cannot finish that update cycle before a new page, and therefore, another update cycle, has taken place. The API of webkit-gtk could allow to get an ID for the popup request, exit the callback with only the promise the answer will be given at some point, and do the actual work outside of the callback. But it doesn't. And there will always be APIs like that and we need to find how to handle this. Regards, Adrien Nader