1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
= The functorial interface =
TyXML provides a functorial interface to implement HTML and SVG on top
of any XML implementation. This is used heavily by Eliom to implement
the ##F## and ##D## modules, providing respectively a functional and
a DOM version of the HTML implementation.
==@@id="make"@@ The ##Make## functors ==
These interfaces are available in the modules
<<a_api | module Html_f >> and <<a_api | module Svg_f >>.
They provide a functor ##Make## which takes a concrete implementation
of Xml following <<a_api | module Xml_sigs.T >>. A default
implementation is provided by the <<a_api | module Tyxml.Xml >> module. The
functor <<a_api | module Html_f.Make >> also needs an Svg
implementation that one can obtain, for example, with the functor
<<a_api | module Svg_f.Make >>. The ##Xml## always needs to provide a
module ##W## of type <<a_api | module Xml_wrap.T >>, along with types
<<code language="ocaml"| type 'a wrap = 'a W.t >> and
<<code language="ocaml"| type 'a list_wrap = 'a W.tlist >>.
The purpose of the ##Wrap## module is explained in the next section.
==@@id="wrap"@@ Wrapping up the nodes ==
The module <<a_api | module Xml_sigs.T.W >> allows to wrap Xml elements
in a monad ##'a t##. A good example of application is the ##R##
modules with reactive nodes in Eliom. Here is the simplified signature
of the {{{div}}} element:
<<code language="ocaml"|R.div : 'a elt list t -> div elt>>
{{{t}}} will wrap the input of every ##Xml## node and be
integrated in the resulting node.
The ##W## module needs to implement operations over the type of nodes,
and provides a special type for lists of nodes. The ##W## module
additionally provides a type ##(-'a, 'b) ft## for (wrapped) functions,
whose purpose is explained in the next section. In most cases, it is
sufficient to define:
<<code language="ocaml"| (-'a, 'b) ft = 'a -> 'b>>
An identity wrapper, <<a_api| module Xml_wrap.NoWrap >>, is
provided. <<a_api| module Xml_wrap.NoWrap >> can be used to apply the
functor without wrapping the elements.
==@@id="wrapped_functions"@@ The ##Make_with_wrapped_functions## functors ==
The ##Make_with_wrapped_functions## functors (available in the modules
<<a_api | module Html_f >> and <<a_api | module Svg_f >>) differ from
the ##Make## functors by requiring an additional argument ##C## (of
type <<a_api | module Html_f.Wrapped_functions >> and
<<a_api | module Svg_f.Wrapped_functions >> respectively).
The ##C## functor argument defines a type ##(-'a, 'b) ft##, and a
collection of ##ft## values. For applying the functor, the type
constraint
<<code language="ocaml"| ('a, 'b) Xml.W.ft = ('a, 'b) C.ft>>
needs to be satisfied. The ##ft## values are wrapped functions that
the functor uses internally to operate on wrapped elements.
The motivation of providing the ##Make_with_wrapped_functions## is as
follows. Certain monads ##'a t## can only be operated upon by wrapped
functions, and not by plain OCaml functions. The wrapped functions
cannot be produced internally by TyXML, and thus have to be provided
to the functor. Our intended application is with Eliom shared (i.e.,
client-server) signals. Such signals can only be operated upon with
Eliom shared functions.
The ##Make## functors simply apply the ##Make_with_wrapped_functions##
functors with TyXML-provided ##Wrapped_functions## modules. The latter
modules do not wrap the functions, i.e., they satisfy:
<<code language="ocaml"|(-'a, 'b) ft = 'a -> 'b>>.
==@@id="sig"@@ Exporting the correct signature ==
In order to help export the correct signature after a functor
application, two signature functors are provided:
<<a_api| module Svg_sigs.Make >> and <<a_api| module Html_sigs.Make >>.
As an example of use, let us look at the module <<a_api| module Tyxml.Svg >>.
Here is the definition of the module:
<<code language="ocaml"|module M = Svg_f.Make(Tyxml_xml)>>
In this case, the declaration in the interface file should look like
this: <<code language="ocaml"|module M : Svg_sigs.Make(Tyxml_xml).T>>
The signature functor <<a_api| module Svg_sigs.Make >> contains only a
signature ##T##, which is equal to <<a_api| module Svg_sigs.T >>, but
exports various equalities with the module ##Xml##.
You should **always** use a signature functor to give the type of a
module produced by a functor application. It will ensure that exactly
the right type equalities are exported and will naturally keep track
of changes in TyXML.
There are some important notes about these signature functors:
* {{{module M : Svg_sigs.Make(Tyxml_xml).T}}} doesn't mean that {{{M.Xml}}}
is a submodule of {{{Xml}}}. It only means that the types {{{uri}}},
{{{event_handler}}}, {{{mouse_event_handler}}},
{{{keyboard_event_handler}}}, {{{touch_event_handler}}},
{{{attrib}}} and {{{elt}}} are the
same in both modules. This is useful when not exporting the exact
module that was used in the functor, but another (smaller and
simpler) module. This is the case in <<a_api project="js_of_ocaml" subproject="js_of_ocaml-tyxml" | module Js_of_ocaml_tyxml.Tyxml_js.R >>,
for example.
* <<a_api | module Html_f >> and <<a_api | module Svg_f >> functors
export two additional equalities, {{{+'a elt = Xml.elt}}} and
{{{+'a attrib = Xml.attrib}}}. These equalities **should never be
exported in a public interface**. Exporting them would break HTML
typing by allowing to build invalid HTML trees. These equalities
are useful internally, for example in Eliom they are used to make
{{{F.elt}}}, {{{D.elt}}} and {{{R.elt}}} equals.
|