| 1 |
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
| 2 |
<page name="cduce_ws">
|
| 3 |
|
| 4 |
<title>CDuce_WS</title>
|
| 5 |
<left>
|
| 6 |
<p>
|
| 7 |
This page is an introduction to CDuce_WS.
|
| 8 |
</p>
|
| 9 |
<boxes-toc/>
|
| 10 |
<p>See also:</p>
|
| 11 |
<local-links href="index,memento,manual,tutorial"/>
|
| 12 |
</left>
|
| 13 |
|
| 14 |
<box title="Introduction" link="intro">
|
| 15 |
|
| 16 |
<p>
|
| 17 |
CDuce_WS is a library for Web Services prototyping. It can be used alone
|
| 18 |
for the creation of clients programs, and
|
| 19 |
with <a href="http://ocsigen.org/occduce">OcCDuce</a> for servers.
|
| 20 |
</p>
|
| 21 |
<p> Basically, CDuce_WS is a CDuce representation of the SOAP protocol
|
| 22 |
structures (SOAP Envelope, SOAP Encoding, etc.) with some helper
|
| 23 |
functions to ease the programmation of Web Services.
|
| 24 |
Additionally, a WSDL structure is also provided, as well as some
|
| 25 |
functions (adapted from
|
| 26 |
<a href="http://forge.ocamlcore.org/projects/ocsoap/">OCSoap</a>) to
|
| 27 |
extract useful information from a WSDL file.
|
| 28 |
</p>
|
| 29 |
</box>
|
| 30 |
<box title="Installation" link="install">
|
| 31 |
<p>
|
| 32 |
CDuce_WS requires:
|
| 33 |
</p>
|
| 34 |
<ul>
|
| 35 |
<li><a href="http://www.cduce.org">CDuce</a></li>
|
| 36 |
<li><a href="http://sourceforge.net/projects/ocurl/">Ocurl</a></li>
|
| 37 |
<li><a href="http://projects.camlcity.org/projects/findlib.html">ocamlfind</a></li>
|
| 38 |
<li>and <a href="http://ocsigen.org/occduce/">OcCduce</a> if you want to use CDuce_WS to create
|
| 39 |
servers.</li>
|
| 40 |
</ul>
|
| 41 |
<p> Alternatively, you can use
|
| 42 |
<a href="http://godi.camlcity.org/godi/index.html">GODI</a> to
|
| 43 |
compile and install all the dependencies.
|
| 44 |
</p>
|
| 45 |
<p>You must first download the archive
|
| 46 |
file of <a href="download/cduce_ws-0.1.tar.gz"> version 0.1</a> and
|
| 47 |
untar it in a temporary directory.
|
| 48 |
</p>
|
| 49 |
<p>
|
| 50 |
To compile and install it, executes
|
| 51 |
<code>make</code> and <code>make install</code> in the base directory.
|
| 52 |
</p>
|
| 53 |
<p>
|
| 54 |
You should look in the <b>examples</b> directory and try to compile
|
| 55 |
and install them, once CDuce_WS is installed.
|
| 56 |
</p>
|
| 57 |
</box>
|
| 58 |
<box title="First example" link="first">
|
| 59 |
<p>
|
| 60 |
This example corresponds to the example in the directory: examples/echo/.
|
| 61 |
<br/>
|
| 62 |
It is a very simple service that receives an integer from a client and
|
| 63 |
sends it back.
|
| 64 |
<br/>
|
| 65 |
The first file, <b>common.cd</b>, contains all the declarations used by
|
| 66 |
the client and the server, and is included by all other files.
|
| 67 |
The type <b>Msg</b> corresponds to the information that a client sends
|
| 68 |
to the server, while the type <b>Answer</b> embeds the response from
|
| 69 |
the server to the client.
|
| 70 |
</p>
|
| 71 |
<sample><![CDATA[(* file common.cd *)
|
| 72 |
|
| 73 |
namespace on;;
|
| 74 |
using SoapEnv = "soapenv";;
|
| 75 |
namespace ns = "urn:echo"
|
| 76 |
|
| 77 |
type Msg = <ns:echoInteger>[
|
| 78 |
<inputInteger>Latin1
|
| 79 |
];;
|
| 80 |
|
| 81 |
type Answer = <ns:echoIntegerResponse> [
|
| 82 |
<return>Latin1
|
| 83 |
];;
|
| 84 |
]]></sample>
|
| 85 |
|
| 86 |
<p>The next file, <b>ws_echo.cd</b>, contains the code necessary
|
| 87 |
to register a web service in Ocsigen, via OcCduce.
|
| 88 |
<br/>
|
| 89 |
The call to <b>Lib.register_webservice</b> registers 2
|
| 90 |
services in Ocsigen:
|
| 91 |
</p>
|
| 92 |
<ul>
|
| 93 |
<li> <code>wsEcho</code>, which is called when a client requests the URL with
|
| 94 |
the POST method. The first argument corresponds to the POST
|
| 95 |
parameters, the second argument to the GET parameters and the third
|
| 96 |
one is the content of the request. POST and GET parameters are
|
| 97 |
empty in case of a web service. The content of the request is the
|
| 98 |
SOAP message as a string (actually a Latin1 string).</li>
|
| 99 |
<li> <code>wsError</code>, which is called if the client uses the
|
| 100 |
GET method (hopefully, it should not happen). </li>
|
| 101 |
</ul>
|
| 102 |
<p><b>NOTE1:</b> it is not possible to load more than once the same
|
| 103 |
CDuce program in OcCDuce. Therefore, you should never give the same
|
| 104 |
name to the source files of different services if you plan to load
|
| 105 |
them simultaneously.
|
| 106 |
</p>
|
| 107 |
<p>
|
| 108 |
<b>NOTE2:</b> Sometimes, the namespaces used by the client and the
|
| 109 |
server do not match. This problem can be avoided by using 3
|
| 110 |
functions: <code>Cduce_ws.load_xml_subst</code>,
|
| 111 |
<code>Cduce_ws.print_xml_subst</code>,
|
| 112 |
and <code>Occduce_lib.set_subst_uri</code>.
|
| 113 |
Both load_xml_subst and print_xml_subst are equivalent to load_xml and
|
| 114 |
print_xml with an additional parameter, a list of pairs of URIs. For
|
| 115 |
each pair of URIs, the first one is the original (i.e. used in the
|
| 116 |
in the other parameter), and the second URI is the new URI. Every
|
| 117 |
original URI will be substituted by the new one in the returned
|
| 118 |
value.
|
| 119 |
<br/>
|
| 120 |
<code>set_subst_uri</code> also takes a list of pairs of URIs as a
|
| 121 |
parameter, and it must be used <b>before</b> returning the
|
| 122 |
XML value in the server. The namespaces of the XML value returned
|
| 123 |
will be converted before it is sent to the client.
|
| 124 |
</p>
|
| 125 |
|
| 126 |
<sample><![CDATA[(* file ws_echo.cd *)
|
| 127 |
|
| 128 |
include "../common.cd"
|
| 129 |
using Echo = "echo";;
|
| 130 |
using Lib = "occduce_lib";;
|
| 131 |
|
| 132 |
let wsError (_ : []) : AnyXml =
|
| 133 |
SoapEnv.std_soap_fault;;
|
| 134 |
|
| 135 |
let wsEcho (_ : []) (_ : []) (content : Latin1) : AnyXml =
|
| 136 |
Occduce.debug_string "wsEcho\n";
|
| 137 |
try
|
| 138 |
(let a = Echo.echo content in
|
| 139 |
Occduce.set_subst_uri
|
| 140 |
[("http://schemas.xmlsoap.org/soap/envelope",
|
| 141 |
"http://schemas.xmlsoap.org/soap/envelope/")];
|
| 142 |
a)
|
| 143 |
with err & Latin1 ->
|
| 144 |
Occduce.debug_string [ 'Cduce exception: ' !err '\n'];
|
| 145 |
exit 2;;
|
| 146 |
|
| 147 |
let ws_echo =
|
| 148 |
Lib.register_webservice
|
| 149 |
{ path = ["ws_echo"]; serviceName = "wsEcho" }
|
| 150 |
wsEcho wsError
|
| 151 |
]]></sample>
|
| 152 |
<p>
|
| 153 |
The next file, <b>echo.cd</b>, contains the processing of the SOAP
|
| 154 |
message and the generation of the response message.
|
| 155 |
<br/>
|
| 156 |
<code>load_xml_subst</code> converts the string containing the SOAP
|
| 157 |
message in a SOAP XML structure after the substitution of the URIs
|
| 158 |
(obviously, using namespaces substitution in the server and client
|
| 159 |
code is not useful).
|
| 160 |
<br/>
|
| 161 |
The body of the SOAP structure, a list of AnyXml, is extracted with the
|
| 162 |
function <code>SoapEnv.get_body</code>, while the
|
| 163 |
function <code>SoapEnv.hd</code> returns the first XML structure in
|
| 164 |
the list.
|
| 165 |
<br/>
|
| 166 |
After the processing of the message and the creation of the response
|
| 167 |
message, <code>SoapEnv.add_envelope</code> embeds it in a SOAP envelope.
|
| 168 |
</p>
|
| 169 |
<sample><![CDATA[(* file echo.cd *)
|
| 170 |
|
| 171 |
include "../common.cd"
|
| 172 |
|
| 173 |
let echo (s : Latin1) : SoapEnv.Envelope =
|
| 174 |
let xml = Cduce_ws.load_xml_subst [ 'string:' !s]
|
| 175 |
[("http://schemas.xmlsoap.org/soap/envelope",
|
| 176 |
"http://schemas.xmlsoap.org/soap/envelope/")]
|
| 177 |
in
|
| 178 |
let res = SoapEnv.hd (SoapEnv.get_body xml) in
|
| 179 |
let tmp :? Msg = res in
|
| 180 |
let number = (match tmp with <ns:echoInteger>[<inputInteger>n] -> n)
|
| 181 |
in
|
| 182 |
let msg =
|
| 183 |
<ns:echoIntegerResponse>[
|
| 184 |
<return> number
|
| 185 |
] in
|
| 186 |
SoapEnv.add_envelope msg;;
|
| 187 |
]]></sample>
|
| 188 |
<p>
|
| 189 |
The last file, <b>client.cd</b>, executes the following steps:
|
| 190 |
</p>
|
| 191 |
<ul>
|
| 192 |
<li>it creates a message of type Msg. <br/>
|
| 193 |
<b>NOTE: </b>Since we need to
|
| 194 |
use <code>load_xml</code> or <code>load_xml_subst</code> to convert the string in XML, it is
|
| 195 |
not possible to use primitive types such as integer in the types
|
| 196 |
transmitted between the client and the server. We can only use
|
| 197 |
Latin1 strings</li>
|
| 198 |
<li>the message is embedded in a SOAP envelope
|
| 199 |
using <code>SoapEnv.add_envelope</code>, and converted as a string by
|
| 200 |
<code>print_xml_subst</code>.</li>
|
| 201 |
<li>the string message is sent to the server
|
| 202 |
with <code>Cduce_ws.send</code>, where the parameters are: the
|
| 203 |
message string, the host address, the soap action, and the content
|
| 204 |
type.
|
| 205 |
<br/> The returned value of <code>send</code> is a Latin1 string
|
| 206 |
with the response message in SOAP.
|
| 207 |
</li>
|
| 208 |
<li>the response message is converted back in XML by <code>load_xml_subst</code>.</li>
|
| 209 |
<li>the body part of the SOAP response is extracted
|
| 210 |
using <code>SoapEnv.get_body</code>, and printed.</li>
|
| 211 |
</ul>
|
| 212 |
<sample><![CDATA[(* file client.cd *)
|
| 213 |
|
| 214 |
include "../common.cd"
|
| 215 |
let msg : Msg =
|
| 216 |
<ns:echoInteger>[
|
| 217 |
<inputInteger>['1234567890']
|
| 218 |
];;
|
| 219 |
|
| 220 |
let _ =
|
| 221 |
let string_msg = Cduce_ws.print_xml_subst (SoapEnv.add_envelope msg)
|
| 222 |
[("http://schemas.xmlsoap.org/soap/envelope/",
|
| 223 |
"http://schemas.xmlsoap.org/soap/envelope")]
|
| 224 |
in
|
| 225 |
let answer_string = Cduce_ws.send string_msg
|
| 226 |
"http://localhost/ws/ws_echo" "" "text/xml; charset=utf-8" in
|
| 227 |
print [ 'CDuce: ' !answer_string '\n'];
|
| 228 |
let xml = Cduce_ws.load_xml_subst [ 'string:' !answer_string]
|
| 229 |
[("http://schemas.xmlsoap.org/soap/envelope",
|
| 230 |
"http://schemas.xmlsoap.org/soap/envelope/")]
|
| 231 |
in
|
| 232 |
print (string_of (SoapEnv.get_body xml));;
|
| 233 |
]]></sample>
|
| 234 |
</box>
|
| 235 |
<box title="A more advanced example" link="calc">
|
| 236 |
<p>
|
| 237 |
This example corresponds to the example in the directory: examples/calc/ in the CDuce_WS distribution.
|
| 238 |
<br/>
|
| 239 |
It is a simple calculator over integer values that receive two values
|
| 240 |
with an operator from a client and return the result.
|
| 241 |
<br/>
|
| 242 |
The first file, <b>common.cd</b>, contains all the declarations used by
|
| 243 |
the client and the server, and is included by all other files.
|
| 244 |
The type <b>Operation</b> corresponds to all possible operations that
|
| 245 |
a client can send
|
| 246 |
to the server, while the type <b>Response</b> embeds the result sent by
|
| 247 |
the server to the client.
|
| 248 |
</p>
|
| 249 |
|
| 250 |
<sample><![CDATA[(* file common.cd *)
|
| 251 |
|
| 252 |
namespace on;;
|
| 253 |
namespace ns = "urn:calc"
|
| 254 |
using SoapEnv = "soapenv";;
|
| 255 |
|
| 256 |
type Params = [ <a>Latin1 <b>Latin1 ];;
|
| 257 |
type AddIn = <ns:add>Params;;
|
| 258 |
type SubIn = <ns:sub>Params;;
|
| 259 |
type MulIn = <ns:mul>Params;;
|
| 260 |
type DivIn = <ns:div>Params;;
|
| 261 |
type Result = <result>Latin1;;
|
| 262 |
type AddOut = <ns:addResponse>[Result];;
|
| 263 |
type SubOut = <ns:subResponse>[Result];;
|
| 264 |
type MulOut = <ns:mulResponse>[Result];;
|
| 265 |
type DivOut = <ns:divResponse>[Result];;
|
| 266 |
type Operation = AddIn | SubIn | MulIn | DivIn;;
|
| 267 |
type Response = AddOut | SubOut | MulOut | DivOut;;
|
| 268 |
]]></sample>
|
| 269 |
|
| 270 |
<p>The next file, <b>ws_calc.cd</b>, contains the code necessary
|
| 271 |
to register a web service in Ocsigen, via OcCduce.
|
| 272 |
<br/>
|
| 273 |
It is quite similar to the code from the previous example.
|
| 274 |
</p>
|
| 275 |
|
| 276 |
<sample><![CDATA[(* file ws_calc.cd *)
|
| 277 |
|
| 278 |
include "../common.cd"
|
| 279 |
using Calc = "calc";;
|
| 280 |
using Lib = "occduce_lib";;
|
| 281 |
|
| 282 |
let wsError (_ : []) : AnyXml =
|
| 283 |
SoapEnv.std_soap_fault;;
|
| 284 |
|
| 285 |
let wsCalc (_ : []) (_ : []) (content : Latin1) : AnyXml =
|
| 286 |
Occduce.debug_string "wsCalc\n";
|
| 287 |
try (Calc.calc content)
|
| 288 |
with err & Latin1 ->
|
| 289 |
Occduce.debug_string [ 'Cduce exception: ' !err '\n'];
|
| 290 |
exit 2;;
|
| 291 |
|
| 292 |
let ws_calc =
|
| 293 |
Lib.register_webservice
|
| 294 |
{ path = ["ws_calc"]; serviceName = "wsCalc" }
|
| 295 |
wsCalc wsError
|
| 296 |
]]></sample>
|
| 297 |
|
| 298 |
<p>
|
| 299 |
The next file, <b>calc.cd</b>, contains the processing of the SOAP
|
| 300 |
message and the generation of the response message.
|
| 301 |
<br/>
|
| 302 |
<code>load_xml</code> converts the string containing the SOAP
|
| 303 |
message in a SOAP XML structure which is passed
|
| 304 |
to <code>SoapEnv.get_body</code> and <code>SoapEnv.hd</code> to
|
| 305 |
extract the content of the body of the SOAP structure.
|
| 306 |
<br/>
|
| 307 |
The function <code>compute</code> processes the message and returns a
|
| 308 |
result in a response message, which is embedded in a SOAP envelope by <code>SoapEnv.add_envelope</code>.
|
| 309 |
</p>
|
| 310 |
|
| 311 |
<sample><![CDATA[(* file calc.cd *)
|
| 312 |
|
| 313 |
include "../common.cd"
|
| 314 |
using SoapEnv = "soapenv";;
|
| 315 |
|
| 316 |
(* For a finer-grained type-checking, declare the
|
| 317 |
function compute with the following type declaration:
|
| 318 |
|
| 319 |
let compute ( AddIn -> AddOut,
|
| 320 |
SubIn -> SubOut,
|
| 321 |
MulIn -> MulOut,
|
| 322 |
DivIn -> DivOut )
|
| 323 |
| <ns:add>[<a>a <b>b] ->
|
| 324 |
<ns:addResponse>[<result>(string_of ((int_of a) + (int_of b)))]
|
| 325 |
| <ns:sub>[<a>a <b>b] ->
|
| 326 |
<ns:subResponse>[<result>(string_of ((int_of a) - (int_of b)))]
|
| 327 |
| <ns:mul>[<a>a <b>b] ->
|
| 328 |
<ns:mulResponse>[<result>(string_of ((int_of a) * (int_of b)))]
|
| 329 |
| <ns:div>[<a>a <b>b] ->
|
| 330 |
<ns:divResponse>[<result>(string_of ((int_of a) div (int_of b)))];;
|
| 331 |
*)
|
| 332 |
|
| 333 |
let compute (op : Operation) : Response =
|
| 334 |
match op with
|
| 335 |
<ns:add>[<a>a <b>b] ->
|
| 336 |
<ns:addResponse>[<result>(string_of ((int_of a) + (int_of b)))]
|
| 337 |
| <ns:sub>[<a>a <b>b] ->
|
| 338 |
<ns:subResponse>[<result>(string_of ((int_of a) - (int_of b)))]
|
| 339 |
| <ns:mul>[<a>a <b>b] ->
|
| 340 |
<ns:mulResponse>[<result>(string_of ((int_of a) * (int_of b)))]
|
| 341 |
| <ns:div>[<a>a <b>b] ->
|
| 342 |
<ns:divResponse>[<result>(string_of ((int_of a) div (int_of b)))];;
|
| 343 |
|
| 344 |
let calc (s : Latin1) : SoapEnv.Envelope =
|
| 345 |
let xml = load_xml [ 'string:' !s] in
|
| 346 |
let res = SoapEnv.hd (SoapEnv.get_body xml) in
|
| 347 |
let op :? Operation = res in
|
| 348 |
let answer = compute op in
|
| 349 |
SoapEnv.add_envelope answer;;
|
| 350 |
]]></sample>
|
| 351 |
|
| 352 |
<p>
|
| 353 |
The last file, <b>client.cd</b>, executes the following steps:
|
| 354 |
</p>
|
| 355 |
<ul>
|
| 356 |
<li>it creates a message of type Operation. </li>
|
| 357 |
<li>the message is embedded in a SOAP envelope
|
| 358 |
using <code>SoapEnv.add_envelope</code></li>
|
| 359 |
<li>it is then converted as a string by
|
| 360 |
<code>print_xml</code> and sent to the server
|
| 361 |
with <code>Cduce_ws.send</code>, where the parameters are: the
|
| 362 |
message string, the host address, the soap action, and the content
|
| 363 |
type.
|
| 364 |
<br/> The returned value of <code>send</code> is a Latin1 string
|
| 365 |
with the response message in SOAP.
|
| 366 |
</li>
|
| 367 |
<li>the response message is converted back in XML by <code>load_xml</code>.</li>
|
| 368 |
<li>the body part of the SOAP response is extracted
|
| 369 |
using <code>SoapEnv.get_body</code>, and printed.</li>
|
| 370 |
</ul>
|
| 371 |
|
| 372 |
<sample><![CDATA[(* file client.cd *)
|
| 373 |
|
| 374 |
include "../common.cd"
|
| 375 |
using SoapEnv = "soapenv";;
|
| 376 |
|
| 377 |
let msg :? Operation = <ns:add>[ <a>"24" <b>"18" ];;
|
| 378 |
|
| 379 |
let _ =
|
| 380 |
let msg_soap = SoapEnv.add_envelope msg in
|
| 381 |
let answer_string = Cduce_ws.send (print_xml msg_soap)
|
| 382 |
"http://localhost/ws/ws_calc" "" "text/xml; charset=utf-8" in
|
| 383 |
print [ 'CDuce: ' !answer_string '\n'];
|
| 384 |
let xml = load_xml [ 'string:' !answer_string ] in
|
| 385 |
let soap_answer :? SoapEnv.Envelope = xml in
|
| 386 |
print (string_of (SoapEnv.get_body xml));;
|
| 387 |
]]></sample>
|
| 388 |
|
| 389 |
</box>
|
| 390 |
|
| 391 |
<box title="WSDL" link="wsdl">
|
| 392 |
<p>
|
| 393 |
You can extract some useful information from WSDL files using
|
| 394 |
functions from the WSDL module.
|
| 395 |
</p>
|
| 396 |
<ul>
|
| 397 |
<li><code>wsdl_load "file.wsdl"</code>: loads a WSDL file in a Wsdl structure.</li>
|
| 398 |
<li><code>wsdl_port_types</code>: extracts the port types from a
|
| 399 |
Wsdl structure.</li>
|
| 400 |
<li><code>show_port_type</code>: prints all the messages of a port type.</li>
|
| 401 |
<li><code>extract_schema</code>: save the xsd schema part of a Wsdl
|
| 402 |
structure in a file.</li>
|
| 403 |
</ul>
|
| 404 |
<p>
|
| 405 |
The following program, <code>wsdl_test.cd</code>, prints the messages
|
| 406 |
from a wsdl file <b>calc.wsdl</b> (you can download it from the <a href="http://gsoap2.sourceforge.net/">gsoap
|
| 407 |
website</a>), and save the xsd part in a file "calc_sch.xsd".
|
| 408 |
</p>
|
| 409 |
<sample><![CDATA[(* wsdl_test.cd *)
|
| 410 |
using WSDL = "wsdl"
|
| 411 |
|
| 412 |
let _ =
|
| 413 |
let w = WSDL.wsdl_load "calc.wsdl" in
|
| 414 |
let port_types = WSDL.wsdl_port_types w in
|
| 415 |
transform port_types with (name,pt) -> WSDL.show_port_type pt name;
|
| 416 |
WSDL.extract_schema w "calc_sch.xsd";;
|
| 417 |
]]></sample>
|
| 418 |
<p>
|
| 419 |
You need to compile and execute <b>wsdl_test.cd</b> with the following command:
|
| 420 |
</p>
|
| 421 |
<sample><![CDATA[
|
| 422 |
cduce --compile -I `ocamlfind query cduce_ws` wsdl_test.cd
|
| 423 |
cduce --run -I `ocamlfind query cduce_ws` wsdl_test.cdo
|
| 424 |
]]></sample>
|
| 425 |
<p>
|
| 426 |
The output of <b>wsdl_test.cd</b> is:
|
| 427 |
</p>
|
| 428 |
<sample><![CDATA[calcPortType
|
| 429 |
<ns:add>[ <a>double <b>double ]
|
| 430 |
-> <ns:addResponse>[ <result>double ]
|
| 431 |
|
| 432 |
<ns:sub>[ <a>double <b>double ]
|
| 433 |
-> <ns:subResponse>[ <result>double ]
|
| 434 |
|
| 435 |
<ns:mul>[ <a>double <b>double ]
|
| 436 |
-> <ns:mulResponse>[ <result>double ]
|
| 437 |
|
| 438 |
<ns:div>[ <a>double <b>double ]
|
| 439 |
-> <ns:divResponse>[ <result>double ]
|
| 440 |
|
| 441 |
<ns:pow>[ <a>double <b>double ]
|
| 442 |
-> <ns:powResponse>[ <result>double ]
|
| 443 |
]]></sample>
|
| 444 |
<p> Which has been translated in the following CDuce code from
|
| 445 |
the <b>calc</b> example:</p>
|
| 446 |
<sample><![CDATA[namespace ns = "urn:calc"
|
| 447 |
|
| 448 |
type Params = [ <a>Latin1 <b>Latin1 ];;
|
| 449 |
type AddIn = <ns:add>Params;;
|
| 450 |
type SubIn = <ns:sub>Params;;
|
| 451 |
type MulIn = <ns:mul>Params;;
|
| 452 |
type DivIn = <ns:div>Params;;
|
| 453 |
type Result = <result>Latin1;;
|
| 454 |
type AddOut = <ns:addResponse>[Result];;
|
| 455 |
type SubOut = <ns:subResponse>[Result];;
|
| 456 |
type MulOut = <ns:mulResponse>[Result];;
|
| 457 |
type DivOut = <ns:divResponse>[Result];;
|
| 458 |
type Operation = AddIn | SubIn | MulIn | DivIn;;
|
| 459 |
type Response = AddOut | SubOut | MulOut | DivOut;;
|
| 460 |
]]></sample>
|
| 461 |
|
| 462 |
<p>
|
| 463 |
Apart from the conversion of <b>double</b> in <b>Latin1</b>, the
|
| 464 |
translation is quite straightforward.
|
| 465 |
</p>
|
| 466 |
</box>
|
| 467 |
</page>
|
| 468 |
|
| 469 |
|