(* The introductory factorial example: the client code and many regression tests *) (* #load "unix.cma";; #load "config.cmo";; #load "wire.cmo";; #load "future.cmo";; #load "rpc_dsl.cmo";; *) open Rpc_dsl;; open BatchFact;; (* First test: compute 5! alone *) let t0 = app fact (int 5);; (* val t0 : int Rpc_dsl.BatchFact.chr = *) (* The phrase `Waiting for a response' indicates the fact of a remote communication. *) let t0v = force t0;; (* Waiting for a response val t0v : int = 120 *) (* Batching two independent requests *) let t1 = app fact (int 7);; (* val t1 : int Rpc_dsl.BatchFact.chr = *) let t2 = app fact (int 4);; (* val t2 : int Rpc_dsl.BatchFact.chr = *) (* Again, the phrase `Waiting for a response' indicates the fact of a remote communication. Although two request were made only one communication with the server has occurred. *) let t12 = (force t1 - force t2);; (* Waiting for a response val t12 : int = 5016 *) (* No communication occurs *) let 5016 = (force t1 - force t2);; (* The server has printed this trace: New connection Evaluating: App Tag:Factorial 7 Evaluating: App Tag:Factorial 4 Responses computed. Replying... which means that the request to compute 7! was received first. *) (* Let's try to switch the evaluation order *) let t1' = app fact (int 7);; let t2' = app fact (int 4);; let t12' = (force t2' - force t1');; (* Waiting for a response val t12' : int = -5016 *) (* Verify the value *) let -5016 = t12';; (* The server's trace is the same as that for t12. It does not matter which 7! or 4! was needed first by the subtraction operation: the client library sends all outstanding requests. Some of them may be computed speculatively, even if their value is not yet needed. The hope is that the value will be needed eventually. Although we use futures, our language remains call-by-value. *) (* Batching dependent computations. We compute (3!)! The result of the first Chourai call is used in the second one. *) let t3 = app fact (int 3);; (* val t3 : int Rpc_dsl.BatchFact.chr = *) (* No communication has occurred yet! *) let t4 = app fact t3;; (* val t4 : int Rpc_dsl.BatchFact.chr = *) (* The _single_ communication has occurred. *) let t4v = force t4;; (* Waiting for a response val t4v : int = 720 *) let 720 = t4v;; (* The server trace New connection from Evaluating: App Tag:Factorial 3 Evaluating: App Tag:Factorial Var1 Responses computed. Replying... shows that two requests have been received in one batch. The second request used the value computed by the first. *) (* Let us use t3 again ... *) let t5 = force (app fact t3);; (* Waiting for a response val t5 : int = 720 *) let 720 = t5;; (* The server trace New connection from Evaluating: App Tag:Factorial 6 Responses computed. Replying... *) (* Once the future is determined, it stays that way. *) (* Indeed, we can try t3 directly. *) let 6 = force t3;; (* The result matches 6; no server communication has occurred *) (* Handling of errors *) let t3' = app fact (int (-1));; let t4' = app fact t3';; let "Skipped due to the earlier: Failure(\"fact on a negative number\")" = try force t4'; assert false with Failure e -> e (* Waiting for a response Exception: Failure "Skipped due to the earlier: Failure(\"fact on a negative number\")". *) (* The server trace New connection from Evaluating: App Tag:Factorial -1 Responses computed. Replying... *) (* We see that evaluating (-1)! ended up in an error. We can check this: *) let "Failure(\"fact on a negative number\")" = try force t3'; assert false with Failure e -> e ;; (* Exception: Failure "Failure(\"fact on a negative number\")". *) (* As the server trace shows, the server aborted the batch and set t4, the dependent value, to raise an exception as well. The exception shows that the value of t4 could not be determined because an earlier operation in a batch has raised an exception. *) (* We now test comparison: a two-argument operation *) (* We also test complex applicative expressions *) let tc11 = app fact (int 4);; let tc1c = app2 lt tc11 (app fact (int 5));; let tc1v = force tc1c;; (* Waiting for a response val tc1v : bool = true *) (* The server trace: New connection Evaluating: App Tag:Factorial 4 Evaluating: App Tag:Factorial 5 Evaluating: App2 Tag:< Var1 Var2 Responses computed. Replying... *) let true = tc1v;; (* Checking conditional (guards) *) (* computing 10000! would lead to stack overflow, and is slow too *) let false = force (guard (bool false) (fun () -> app fact (int 10000)));; let true = force ( guard (app2 lt (app fact (int 4)) (app fact (int 7))) (fun () -> (app2 lt (app fact (int 5)) (app fact (int 8))) ));; Printf.printf "\nAll done\n";;