Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

more on frp (was Re: [Caml-list] Re: Recursion on React.events)

346 views
Skip to first unread message

Daniel Bünzli

unread,
Dec 9, 2009, 11:44:10 AM12/9/09
to Richard Jones, OCaml List
> Personally I've yet to read any comprehensible introduction to FRP.

I agree my documentation can be dry for a newcomer to frp. It's not a
tutorial about frp. Its aim is to answer any questions a proficient
user of the library could have and I hope that it fulfilths at least
that goal.

But frp itself is not that hard to comprehend. Unfortunately the only
things I can point you to is (haskell & scheme) academic papers which
also tend to quickly talk about (nonetheless interesting) semantic and
implementation issues.

Maybe the following along with react's documentation can help you to
get started. It's far from perfect, it was quickly written, just a try.

* Events and signals

Frp manipulates two kinds of values. Events and signals (the latter
are a.k.a. behaviours in many implementations). Events and signals are
values that depend on time.

An event has values (occurences) at specific points in time. For
example it could represent the keysym of the key depressed by a
user. Events can be understood as follows :

type 'a event = time -> 'a option

Applying the function with the current time tells you whether there's
an event occurence or not at that time. In our example whether a key
was depressed or not.

A signal has a value at any point in time. It represents values that
vary continuously like the current value of a slider. The type for
signals can be seen as

type 'a signal = time -> 'a

Applying the function with the current time tells you the current
value of the signal. In our example it tells you the current value of
the slider. In general, state, i.e. anything that you'd fit in a
reference cell or a mutable field is a candidate to become a signal.

As you are an astute reader you will already have remarked that the
following approach could have been taken :

type 'a event = 'a option signal

In fact events and signals are duals. One can be implemented in terms
of the other (signals can be seen as the event occurences of its value changes),
However it makes sense to keep the two concepts separate (code
clarity, efficiency issues, semantic details with comparison).

Now given events or signals, frp provides you combinators to define
new derived events. These combinators are in the modules React.E and
React.S.

For example suppose we are given the following event (for now don't
bother about how it could be defined) :

let keydown : keysym event = ...

which represents keysyms of the keys depressed by a user. We would
like to define an event that corresponds to the character of the
keysym. We have the following translation function :

let char_of_keysym : keysym -> char = fun ks -> ...

So we can define our new event as :

let keychar = E.map char_of_keysym keydown

Every time keydown gets an occurence, keychar gets one. Except the
value of the occurence for keychar is the value of keydown transformed
by char_of_keysym.

Suppose we want to remember the last keysym that was issued. That's a
signal, it is defined at any point in time. For that the S.hold
combinator can be used, it remembers the last occurence of an event :

let last_keysym = S.hold null_keysym keydown

Each time keydown gets an occurence S.hold updates the signal with the
new occurence value. null_keysym is used to initalize the signal (it
needs a value at the time it is created and the keydown event may not
give us one at that time).

Now we would like to concatenate the stream of keychar occurences into
a string. That is we want a string signal that holds at any point in
time the key characters that were depressed so far. It can be defined
by folding over the sequence of keychar occurences :

let istr = S.fold (fun acc c = acc ^ (String.make 1 c)) "" keychar

So istr is a string signal. Its initial value is "". Each time keychar
gets an occurence, the signal is updated by applying the folding
function to the current value of the signal with the event
occurence. The result of the folding function is the new value for the
signal.

Suppose we have two float signals a and b. We want to have a signal
that keeps the sum of a and b at any time. For that we lift the (+.)
function to act from float values to float signals as follows :

let sum = S.l2 (+.) a b

Now whenever a or b changes sum is automatically updated with the sum
of the current value of a or b.

To sum up event and signal combinators take signals and events
arguments and return a new signal or event r. r is said to depend on
the arguments given to the combinator. So effectively what you are
building with combinators is a graph of data dependencies. Think about
it like the dependencies in a spreadsheet (e.g. the sum example
above). What frp gives you is that whenever a value changes in the
graph all the transitive dependencies get automatically updated in a
consistant way (glitches are avoided by updating all dependee before
updating the dependent).

Now the question is how can I update signal values and generate event
occurences, combinators only take other events and signals, they don't
allow to update values or generate occurences ? The answer is primitive
events and signals.

Before moving on. I should stress that the notion of time in react is
purely relative (this happens before that). There's no concrete value of
type time as suggested above, this notion of time is only used as a tool
to define the denotational semantics of events and signals. Also realtime can
be integrated if necessary (e.g. look rtime or the clock example in
the distribution)
but it is not necessary. You define what time means to you (e.g. it
could be the
sucession of server request, the sucession of mouse inputs, etc.).


* Primitives

The only events whose occurence can be generated by the client are
primitive events. The only signals whose values can be updated by the
client are primitive signals. All events and signals that result from
combinators are automatically updated by react. Primitives are the
mechanism to interface the reactive runtime described above with the
outside world (e.g. to define a keysym event).

Read now the "Primitive events and signals" section and come back
after ;

http://erratique.ch/software/react/doc/React#primitives


* Program structure

If you own your main loop I'd say you should more or less proceed as
follows.

1) Identify your primitives (sources of input). Define functions
to create primitives. Or if there's no dynamic creation of primitives
create them directly.

2) Define your reactive system in terms of primitive events and
combinators. Usually at the end of your dataflow graph you will have
effectful events and signals to give feedback to the world (beware,
primitive sending and update functions cannot not be used here to
feedback into the reactive system, depose the updates in a queue if
you really need to do that, but more likely you can use fix point
combinators to avoid that).

3) Define an infinite loop that sources the created primitives as
input is gathered.

4) Run the loop.

Have a look at the snippet of code at the end of this email. It
exposes the a subset of the sdl mouse as signals and events (note that
there's more than one way of exposing that information as a set of events
and signals, this may not be the best solution).

If you need to deal with callbacks then the result of callbacks can be
exposed as events and signals by creating a primitive and using the
primitive sending or updating function in the callback.

I now suggest you try to proceed to the "Basics" section of the
documentation

http://erratique.ch/software/react/doc/React#basics

After have a look at the "Semantics"

http://erratique.ch/software/react/doc/React#sem

You may also have a look at the breakout.ml example of the
distribution (use the version in the repository there are fixes for
xterm & vte based terminals).

http://erratique.ch/software/react/repo/test/breakout.ml


> I'm interested in whether FRP can be used to write Gtk interfaces with
> reduced code complexity. Apparently it can, but I've no idea how.

Once you are more acquainted with the subject. Have a look at the
chapter 6 of Gregory Cooper's thesis [1] it provides strategies to
interface frp with existing object-oriented gui toolkits. Chapter 1
may also be interesting in motivating the frp approach.

Finally, feel free to ask any question,

Best,

Daniel

[1] http://www.cs.brown.edu/~greg/thesis.pdf

open React;;

module Mouse : sig
val pos : (int * int) S.t (* mouse position signal. *)
val up : unit E.t (* event on mouse button up. *)
val down : unit E.t (* event on mouse button down. *)
val input : unit -> unit (* inputs mouse changes until user quits. *)
end = struct
let pos, set_pos = S.create (0, 0)
let down, send_down = E.create ()
let up, send_up = E.create ()

open Sdlevent;;
let rec input () = match wait_event () with
| QUIT -> ()
| MOUSEMOTION e -> set_pos (e.mme_x, e.mme_y); input ()
| MOUSEBUTTONDOWN e
| MOUSEBUTTONUP e when e.mbe_button = Sdlmouse.BUTTON_LEFT ->
(match e.mbe_state with PRESSED -> send_down () | RELEASED -> send_up ());
input ()
| KEYDOWN k when k.keycode = 'q' -> ()
| _ -> input ()
end

_______________________________________________
Caml-list mailing list. Subscription management:
http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
Archives: http://caml.inria.fr
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
Bug reports: http://caml.inria.fr/bin/caml-bugs

Aaron Bohannon

unread,
Dec 9, 2009, 1:33:14 PM12/9/09
to Daniel Bünzli, OCaml List, Richard Jones
On Wed, Dec 9, 2009 at 11:43 AM, Daniel B�nzli
<daniel....@erratique.ch> wrote:
>> Personally I've yet to read any comprehensible introduction to FRP.

> In fact events and signals are duals. One can be implemented in terms


> of the other (signals can be seen as the event occurences of its value changes),
> However it makes sense to keep the two concepts separate (code
> clarity, efficiency issues, semantic details with comparison).

> Suppose we want to remember the last keysym that was issued. That's a


> signal, it is defined at any point in time. For that the S.hold
> combinator can be used, it remembers the last occurence of an event :
>
> �let last_keysym = S.hold null_keysym keydown
>
> Each time keydown gets an occurence S.hold updates the signal with the
> new occurence value. null_keysym is used to initalize the signal (it
> needs a value at the time it is created and the keydown event may not
> give us one at that time).
>
> Now we would like to concatenate the stream of keychar occurences into
> a string.

Your example perfectly illustrates what makes understanding FRP hard:
when I type the string "programming", should I expect to get a signal
with a value of "programming" or "programing"?

Signals without events have an semantics that seems straightforward
(to me). But I have very little intuition for events, as represented
by a type such as "time -> 'a option", and their conversion to and
from signals.

Also, I like the idea of making different choices for what "time" is,
but doesn't the semantics of FRP depend on time being continuous?

- Aaron

Aaron Bohannon

unread,
Dec 10, 2009, 1:25:22 AM12/10/09
to Daniel Bünzli, OCaml List, Richard Jones
On Wed, Dec 9, 2009 at 11:16 PM, Daniel B�nzli
<daniel....@erratique.ch> wrote:
>> Your example perfectly illustrates what makes understanding FRP hard:
>> when I type the string "programming", should I expect to get a signal
>> with a value of "programming" or "programing"?
>
> Note, you don't get any signal here, we are talking about an _event_.
> So the sequence of event occurences you will see is 'p', 'r', 'o',
> 'g', 'r', 'a', 'm', 'm', 'i', 'n', 'g'. Now if you fold over these
> occurences as I suggested to make a string signal, the sequence of
> changes in the signal you will see is "", "p", "pr", "pro", "prog",
> "progr", "progra", "program", "programm", "programmi", "programmin",
> "programming" (you could have chosen a shorter word, that was painful
> to write).

I apologize. I read your message too quickly and thought that you
were using "last_keysym" to define "istr". (And I chose the word
because it was part of "FRP". :)

> So the function time -> 'a option returns a value if there's an
> occurence of the event at that time (e.g. if the
> user pressed a key at that time) and None otherwise.

Yes, (I think) I did understand what the type "time -> 'a option" was
trying to represent. It just doesn't seem a very good match for the
concept. It would appear (from the type) that events could have
nonzero duration, but that doesn't match my intuition of events.

Daniel Bünzli

unread,
Dec 10, 2009, 2:14:42 AM12/10/09
to Aaron Bohannon, OCaml List, Richard Jones
> Your example perfectly illustrates what makes understanding FRP hard:
> when I type the string "programming", should I expect to get a signal
> with a value of "programming" or "programing"?

Note, you don't get any signal here, we are talking about an _event_.


So the sequence of event occurences you will see is 'p', 'r', 'o',
'g', 'r', 'a', 'm', 'm', 'i', 'n', 'g'. Now if you fold over these
occurences as I suggested to make a string signal, the sequence of
changes in the signal you will see is "", "p", "pr", "pro", "prog",
"progr", "progra", "program", "programm", "programmi", "programmin",
"programming" (you could have chosen a shorter word, that was painful
to write).

> Signals without events have an semantics that seems straightforward


> (to me).  But I have very little intuition for events, as represented
> by a type such as "time -> 'a option", and their conversion to and
> from signals.

I'm not sure but maybe your problem is just a terminology thing.
Because in frp what is called an event confusignly represents many
events, it represents event occurences of a kind of event (e.g. the
keystrokes of a keyboard).

So the function time -> 'a option returns a value if there's an
occurence of the event at that time (e.g. if the
user pressed a key at that time) and None otherwise.

> Also, I like the idea of making different choices for what "time" is,


> but doesn't the semantics of FRP depend on time being continuous?

I wouldn't say it "depends", it makes you believe, that's the way you
should think about when you program. But in the programming world
everything is eventually discrete and your primitive input is giving
the pace. Have a read at the section "Continuity" there :

http://erratique.ch/software/react/doc/React#sigsem

Best,

Daniel

Rich Neswold

unread,
Dec 12, 2009, 10:04:57 AM12/12/09
to Daniel Bünzli, OCaml List
On Wed, Dec 9, 2009 at 10:43 AM, Daniel Bünzli
<daniel....@erratique.ch>wrote:

> Maybe the following along with react's documentation can help you to
> get started. It's far from perfect, it was quickly written, just a try.
>

Wow! Thank you for your effort!

Your exchange yesterday introduced me to your FRP library and I spent some
time reading the documentation. This latest posting really helped to fill in
more of the details.

Thanks again,

--
Rich

Google Reader: https://www.google.com/reader/shared/rich.neswold
Jabber ID: ri...@neswold.homeunix.net

0 new messages