{0:wrapping Wrapping}

Reading this chapter is not mandatory for writing Eliom
applications. However, it is worth reading if you are planning to hack
on the Eliom codebase.




{1 Basics}

The server side of Eliom can communicate to the client other kinds of
data than the raw XML contents of the pages. The wrapper mechanism is
used to allow the browser side to access to the contents of variables
declared on server side. For instance when we write
{@ocaml[ ignore [%client (Dom_html.window##alert(Js.string ~%text) : unit)] ]}
the contents of the [text] variable is sent along the page for the client code to access it.

Server side, when
{@ocaml[ [%client (Dom_html.window##alert(Js.string ~%text) : unit)] ]}
is executed, the variable [text] is registered into a table and an id is associated to it.
This table will contain all the data references by variables annotated with [~%] in a page, and
will be sent marshalled to the client. On client side the id will be used to retrieve [text].

Since all data are sent in one table, if a variable is referenced
multiples times, it will be sent only once, and sharing will be preserved:

{@ocaml[let a = ref 0 in
let b = (1,a) in
ignore [%client
  ((~%a := 42;
    Dom_html.window##alert(snd ~%b |> string_of_int |> Js.string))
   : unit)
]
]}

This code will display 42. After being sent, the client and server
side values are distinct: the server side version of [a] won't be
modified by the client side affectation and conversely the client side
value won't change if [a] is changed later on server side.

{1 Special types}

{2 Custom wrappers}

Usually, client and server side values are represented the same way,
and it is sufficient to only copy their content ( marshalled ) to the
client. But certain types can't be transmitted this easily: for
instance, services.

Those values must be transformed before marshalling: We need for this to use
custom wrappers. This wrapping mechanism is defined in
{!Eliom_wrap}.

Before sending, the values goes thought {!Eliom_wrap.wrap}
which transform marked values. A value marked is a value which have as its last field
a value of type {!Eliom_wrap.wrapper}. For instance
{@ocaml[type marked_tupple =
  int * ... * marked_tupple Eliom_wrap.wrapper
type marked_record =
  { f1 : int;
    ...
    fn : marked_record Eliom_wrap.wrapper }
]}
but not
{@ocaml[type not_marked_tupple = int * ... * marked_tupple Eliom_wrap.wrapper * float
type not_marked_tupple = int * ... * (int * marked_tupple Eliom_wrap.wrapper)
type not_marked_tupple = int * ... * marked_tupple Eliom_wrap.wrapper list
type not_marked_record =
     { f1 : int;
          ...
       fn : marked_record Eliom_wrap.wrapper;
       fk : float; }]}

A wrapper is created by the {!Eliom_wrap.create_wrapper}
function. It takes a function as parameter which will be called to
transform the value during the wrapping. There is also a special
wrapper {!Eliom_wrap.empty_wrapper} which does nothing. It
is useful to stop calling the wrapper on a value: If there is still a
wrapper in a value after its transformation, it will be called another
time, potentially leading to an infinite loop.

For instance

{@ocaml[type v = Fun of unit -> int | Value of int
type wrapped_type = v * wrapped_type Eliom_wrap.wrapper
let wrapper =
  let wrap = function
    | Value i,wrapper -> Value i, Eliom_wrap.empty_wrapper
    | Fun f,wrapper -> Value (f ()), Eliom_wrap.empty_wrapper
  in Eliom_wrap.create_wrapper f
let v = ( Fun (fun () -> 1), wrapper )
let (v', empty_wrapper) = Eliom_wrap.wrap v]}

At that time [v'] will be [Value 1]. Notice that
{!Eliom_wrap.create_wrapper} does not enforce the output
type of the wrapping function to be the same as the input type:
Eliom_wrap is to be use with much caution! Do not use it if you don't
understand how it works, it may lead to unpredictable segmentation faults
and corrupted memory.

{1 Eliom types with predefined custom wrappers}

The Eliom types that are marked are:

- {!Eliom_service.t} transformed to {!Eliom_service.t} (but the client side representation)
- {!Eliom_comet.Channel.t} transformed to {!Lwt_stream.t}
- {!Eliom_react.Up.t} transformed to ['a -> unit]
- {!Eliom_react.Down.t} transformed to ['a React.E.t]
- {!Eliom_bus.t} transformed to {!Eliom_bus.t}
