Mercurial > urweb
changeset 1848:e15234fbb163
Basis.tryRpc
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Tue, 16 Apr 2013 10:55:48 -0400 (2013-04-16) |
parents | 8958b580d026 |
children | 3005c66b70e8 |
files | doc/manual.tex lib/js/urweb.js lib/ur/basis.urs src/core.sml src/core_print.sml src/core_untangle.sml src/core_util.sml src/css.sml src/effectize.sml src/especialize.sml src/jscomp.sml src/mono.sml src/mono_print.sml src/mono_reduce.sml src/mono_util.sml src/monoize.sml src/reduce.sml src/reduce_local.sml src/rpcify.sml src/settings.sig src/shake.sml tests/tryRpc.ur |
diffstat | 22 files changed, 179 insertions(+), 74 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/manual.tex Mon Apr 01 10:13:49 2013 -0400 +++ b/doc/manual.tex Tue Apr 16 10:55:48 2013 -0400 @@ -2157,6 +2157,12 @@ \mt{val} \; \mt{rpc} : \mt{t} ::: \mt{Type} \to \mt{transaction} \; \mt{t} \to \mt{transaction} \; \mt{t} \end{array}$$ +There is an alternate form that uses $\mt{None}$ to indicate that an error occurred during RPC processing, rather than raising an exception to abort this branch of control flow. + +$$\begin{array}{l} + \mt{val} \; \mt{tryRpc} : \mt{t} ::: \mt{Type} \to \mt{transaction} \; \mt{t} \to \mt{transaction} \; (\mt{option} \; \mt{t}) +\end{array}$$ + \subsubsection{Asynchronous Message-Passing} To support asynchronous, ``server push'' delivery of messages to clients, any client that might need to receive an asynchronous message is assigned a unique ID. These IDs may be retrieved both on the client and on the server, during execution of code related to a client.
--- a/lib/js/urweb.js Mon Apr 01 10:13:49 2013 -0400 +++ b/lib/js/urweb.js Tue Apr 16 10:55:48 2013 -0400 @@ -1365,7 +1365,14 @@ window.location = s; } -function rc(prefix, uri, parse, k, needsSig) { +function makeSome(isN, v) { + if (isN) + return {v: v}; + else + return v; +} + +function rc(prefix, uri, parse, k, needsSig, isN) { if (!maySuspend) er("May not 'rpc' in main thread of 'code' for <active>"); @@ -1384,18 +1391,30 @@ if (isok) { var lines = xhr.responseText.split("\n"); - if (lines.length != 2) - whine("Bad RPC response lines"); + if (lines.length != 2) { + if (isN == null) + whine("Bad RPC response lines"); + else + k(null); + } else { + eval(lines[0]); - eval(lines[0]); - - try { - k(parse(lines[1])); - } catch (v) { - doExn(v); + try { + var v = parse(lines[1]); + try { + k(makeSome(isN, v)); + } catch (v) { + doExn(v); + } + } catch (v) { + k(null); + } } } else { - conn(); + if (isN == null) + conn(); + else + k(null); } xhrFinished(xhr);
--- a/lib/ur/basis.urs Mon Apr 01 10:13:49 2013 -0400 +++ b/lib/ur/basis.urs Tue Apr 16 10:55:48 2013 -0400 @@ -206,6 +206,8 @@ val sleep : int -> transaction unit val rpc : t ::: Type -> transaction t -> transaction t +val tryRpc : t ::: Type -> transaction t -> transaction (option t) +(* Returns [None] on error condition. *) (** Channels *)
--- a/src/core.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/core.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008, Adam Chlipala +(* Copyright (c) 2008, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -86,6 +86,8 @@ withtype pat = pat' located +datatype failure_mode = datatype Settings.failure_mode + datatype exp' = EPrim of Prim.t | ERel of int @@ -115,7 +117,7 @@ | ELet of string * con * exp * exp - | EServerCall of int * exp list * con + | EServerCall of int * exp list * con * failure_mode withtype exp = exp' located
--- a/src/core_print.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/core_print.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2011, Adam Chlipala +(* Copyright (c) 2008-2011, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -446,12 +446,12 @@ newline, p_exp (E.pushERel env x t) e2] - | EServerCall (n, es, _) => box [string "Server(", - p_enamed env n, - string ",", - space, - p_list (p_exp env) es, - string ")"] + | EServerCall (n, es, _, _) => box [string "Server(", + p_enamed env n, + string ",", + space, + p_list (p_exp env) es, + string ")"] | EKAbs (x, e) => box [string x, space,
--- a/src/core_untangle.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/core_untangle.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008, Adam Chlipala +(* Copyright (c) 2008, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,7 +48,7 @@ case e of ENamed n => try n | EClosure (n, _) => try n - | EServerCall (n, _, _) => try n + | EServerCall (n, _, _, _) => try n | _ => s end
--- a/src/core_util.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/core_util.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2010, Adam Chlipala +(* Copyright (c) 2008-2010, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -439,6 +439,14 @@ join (String.compare (x1, x2), fn () => pCompare (p1, p2))) (xps1, xps2) +fun fmCompare (fm1, fm2) = + case (fm1, fm2) of + (None, None) => EQUAL + | (None, _) => LESS + | (_, None) => GREATER + + | (Error, Error) => EQUAL + fun compare ((e1, _), (e2, _)) = case (e1, e2) of (EPrim p1, EPrim p2) => Prim.compare (p1, p2) @@ -547,9 +555,10 @@ | (ELet _, _) => LESS | (_, ELet _) => GREATER - | (EServerCall (n1, es1, _), EServerCall (n2, es2, _)) => + | (EServerCall (n1, es1, _, fm1), EServerCall (n2, es2, _, fm2)) => join (Int.compare (n1, n2), - fn () => joinL compare (es1, es2)) + fn () => join (fmCompare (fm1, fm2), + fn () => joinL compare (es1, es2))) | (EServerCall _, _) => LESS | (_, EServerCall _) => GREATER @@ -738,12 +747,12 @@ fn e2' => (ELet (x, t', e1', e2'), loc)))) - | EServerCall (n, es, t) => + | EServerCall (n, es, t, fm) => S.bind2 (ListUtil.mapfold (mfe ctx) es, fn es' => S.map2 (mfc ctx t, fn t' => - (EServerCall (n, es', t'), loc))) + (EServerCall (n, es', t', fm), loc))) | EKAbs (x, e) => S.map2 (mfe (bind (ctx, RelK x)) e,
--- a/src/css.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/css.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2010, Adam Chlipala +(* Copyright (c) 2010, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -260,7 +260,7 @@ in (merge' (sm1, sm2), classes) end - | EServerCall (_, es, _) => expList (es, classes) + | EServerCall (_, es, _, _) => expList (es, classes) and expList (es, classes) = foldl (fn (e, (sm, classes)) => let
--- a/src/effectize.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/effectize.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2009-2010, Adam Chlipala +(* Copyright (c) 2009-2010, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,7 @@ EFfi f => effectful f | EFfiApp (m, x, _) => effectful (m, x) | ENamed n => IM.inDomain (evs, n) - | EServerCall (n, _, _) => IM.inDomain (evs, n) + | EServerCall (n, _, _, _) => IM.inDomain (evs, n) | _ => false fun couldWriteOnload evs = U.Exp.exists {kind = fn _ => false, @@ -69,7 +69,7 @@ fun exp writers readers pushers e = case e of ENamed n => IM.inDomain (pushers, n) - | EServerCall (n, _, _) => IM.inDomain (writers, n) andalso IM.inDomain (readers, n) + | EServerCall (n, _, _, _) => IM.inDomain (writers, n) andalso IM.inDomain (readers, n) | _ => false fun couldWriteWithRpc writers readers pushers = U.Exp.exists {kind = fn _ => false, @@ -80,7 +80,7 @@ case e of EFfi ("Basis", "getCookie") => true | ENamed n => IM.inDomain (evs, n) - | EServerCall (n, _, _) => IM.inDomain (evs, n) + | EServerCall (n, _, _, _) => IM.inDomain (evs, n) | _ => false fun couldReadCookie evs = U.Exp.exists {kind = fn _ => false,
--- a/src/especialize.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/especialize.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2012, Adam Chlipala +(* Copyright (c) 2008-2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -205,7 +205,7 @@ | EWrite e1 => ca depth e1 | EClosure (_, es) => foldl (fn (e, d) => Int.min (ca depth e, d)) maxInt es | ELet (_, _, e1, e2) => Int.min (ca depth e1, ca (depth + 1) e2) - | EServerCall (_, es, _) => foldl (fn (e, d) => Int.min (ca depth e, d)) maxInt es + | EServerCall (_, es, _, _) => foldl (fn (e, d) => Int.min (ca depth e, d)) maxInt es fun enterAbs depth e = case #1 e of @@ -348,11 +348,11 @@ in ((ELet (x, t, e1, e2), loc), st) end - | EServerCall (n, es, t) => + | EServerCall (n, es, t, fm) => let val (es, st) = ListUtil.foldlMap (fn (e, st) => exp (env, e, st)) st es in - ((EServerCall (n, es, t), loc), st) + ((EServerCall (n, es, t, fm), loc), st) end in case getApp e of
--- a/src/jscomp.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/jscomp.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2012, Adam Chlipala +(* Copyright (c) 2008-2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -930,10 +930,21 @@ st) end - | EServerCall (e, t, eff) => + | EServerCall (e, t, eff, fm) => let val (e, st) = jsE inner (e, st) val (unurl, st) = unurlifyExp loc (t, st) + val lastArg = case fm of + None => "null" + | Error => + let + val isN = if isNullable t then + "true" + else + "false" + in + "cons({c:\"c\",v:" ^ isN ^ "},null)" + end in (strcat [str ("{c:\"f\",f:rc,a:cons({c:\"c\",v:\"" ^ Settings.getUrlPrefix () @@ -944,7 +955,7 @@ ^ (case eff of ReadCookieWrite => "true" | _ => "false") - ^ "},null)))))}")], + ^ "}," ^ lastArg ^ ")))))}")], st) end @@ -1231,11 +1242,11 @@ ((ESignalSource e, loc), st) end - | EServerCall (e1, t, ef) => + | EServerCall (e1, t, ef, fm) => let val (e1, st) = exp outer (e1, st) in - ((EServerCall (e1, t, ef), loc), st) + ((EServerCall (e1, t, ef, fm), loc), st) end | ERecv (e1, t) => let
--- a/src/mono.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/mono.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2010, Adam Chlipala +(* Copyright (c) 2008-2010, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -120,7 +120,7 @@ | ESignalBind of exp * exp | ESignalSource of exp - | EServerCall of exp * typ * effect + | EServerCall of exp * typ * effect * failure_mode | ERecv of exp * typ | ESleep of exp | ESpawn of exp
--- a/src/mono_print.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/mono_print.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008, Adam Chlipala +(* Copyright (c) 2008, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -357,7 +357,7 @@ p_exp env e, string ")"] - | EServerCall (n, _, _) => box [string "Server(", + | EServerCall (n, _, _, _) => box [string "Server(", p_exp env n, string ")"] | ERecv (n, _) => box [string "Recv(",
--- a/src/mono_reduce.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/mono_reduce.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008, Adam Chlipala +(* Copyright (c) 2008, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -515,7 +515,7 @@ | ESignalBind (e1, e2) => summarize d e1 @ summarize d e2 | ESignalSource e => summarize d e - | EServerCall (e, _, _) => summarize d e @ [Unsure] + | EServerCall (e, _, _, _) => summarize d e @ [Unsure] | ERecv (e, _) => summarize d e @ [Unsure] | ESleep e => summarize d e @ [Unsure] | ESpawn e => summarize d e @ [Unsure]
--- a/src/mono_util.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/mono_util.sml Tue Apr 16 10:55:48 2013 -0400 @@ -380,12 +380,12 @@ fn e' => (ESignalSource e', loc)) - | EServerCall (s, t, eff) => + | EServerCall (s, t, eff, fm) => S.bind2 (mfe ctx s, fn s' => S.map2 (mft t, fn t' => - (EServerCall (s', t', eff), loc))) + (EServerCall (s', t', eff, fm), loc))) | ERecv (s, t) => S.bind2 (mfe ctx s, fn s' => @@ -510,7 +510,7 @@ | ESignalReturn e1 => appl e1 | ESignalBind (e1, e2) => (appl e1; appl e2) | ESignalSource e1 => appl e1 - | EServerCall (e1, _, _) => appl e1 + | EServerCall (e1, _, _, _) => appl e1 | ERecv (e1, _) => appl e1 | ESleep e1 => appl e1 | ESpawn e1 => appl e1)
--- a/src/monoize.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/monoize.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2012, Adam Chlipala +(* Copyright (c) 2008-2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -4188,7 +4188,7 @@ ((L'.ELet (x, t', e1, e2), loc), fm) end - | L.EServerCall (n, es, t) => + | L.EServerCall (n, es, t, fmode) => let val t = monoType env t val (_, ft, _, name) = Env.lookupENamed env n @@ -4218,7 +4218,7 @@ else L'.ReadOnly - val e = (L'.EServerCall (call, t, eff), loc) + val e = (L'.EServerCall (call, t, eff, fmode), loc) val e = liftExpInExp 0 e val e = (L'.EAbs ("_", unit, unit, e), loc) in
--- a/src/reduce.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/reduce.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2011, Adam Chlipala +(* Copyright (c) 2008-2011, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -804,7 +804,7 @@ (ELet (x, t, e1', exp (UnknownE :: env) e2), loc) end - | EServerCall (n, es, t) => (EServerCall (n, map (exp env) es, con env t), loc) + | EServerCall (n, es, t, fm) => (EServerCall (n, map (exp env) es, con env t, fm), loc) in (*if dangling (edepth' (deKnown env)) r then (Print.prefaces "exp" [("e", CorePrint.p_exp CoreEnv.empty all),
--- a/src/reduce_local.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/reduce_local.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2010, Adam Chlipala +(* Copyright (c) 2008-2010, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -353,7 +353,7 @@ | ELet (x, t, e1, e2) => (ELet (x, con env t, exp env e1, exp (Unknown :: env) e2), loc) - | EServerCall (n, es, t) => (EServerCall (n, map (exp env) es, con env t), loc) + | EServerCall (n, es, t, fm) => (EServerCall (n, map (exp env) es, con env t, fm), loc) fun reduce file = let
--- a/src/rpcify.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/rpcify.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2009, 2012, Adam Chlipala +(* Copyright (c) 2009, 2012-2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,15 +42,22 @@ fun frob file = let - val rpcBaseIds = foldl (fn ((d, _), rpcIds) => - case d of - DVal (_, n, _, (EFfi ("Basis", "rpc"), _), _) => IS.add (rpcIds, n) - | DVal (_, n, _, (ENamed n', _), _) => if IS.member (rpcIds, n') then - IS.add (rpcIds, n) - else - rpcIds - | _ => rpcIds) - IS.empty file + val (rpcBaseIds, trpcBaseIds) = + foldl (fn ((d, _), (rpcIds, trpcIds)) => + case d of + DVal (_, n, _, (EFfi ("Basis", "rpc"), _), _) => + (IS.add (rpcIds, n), trpcIds) + | DVal (_, n, _, (EFfi ("Basis", "tryRpc"), _), _) => + (rpcIds, IS.add (trpcIds, n)) + | DVal (_, n, _, (ENamed n', _), _) => + if IS.member (rpcIds, n') then + (IS.add (rpcIds, n), trpcIds) + else if IS.member (trpcIds, n') then + (rpcIds, IS.add (trpcIds, n)) + else + (rpcIds, trpcIds) + | _ => (rpcIds, trpcIds)) + (IS.empty, IS.empty) file val tfuncs = foldl (fn ((d, _), tfuncs) => @@ -89,7 +96,7 @@ | EApp (e1, e2) => getApp (#1 e1, e2 :: args) | _ => NONE - fun newRpc (trans : exp, st : state) = + fun newRpc (trans : exp, st : state, fm) = case getApp (#1 trans, []) of NONE => (ErrorMsg.errorAt (#2 trans) "RPC code doesn't use a named function or transaction"; @@ -114,16 +121,19 @@ val st = {exported = exported, export_decls = export_decls} - val e' = EServerCall (n, args, ran) + val e' = EServerCall (n, args, ran, fm) in (e', st) end in case e of - EApp ((ECApp ((EFfi ("Basis", "rpc"), _), ran), _), trans) => newRpc (trans, st) + EApp ((ECApp ((EFfi ("Basis", "rpc"), _), ran), _), trans) => newRpc (trans, st, None) + | EApp ((ECApp ((EFfi ("Basis", "tryRpc"), _), ran), _), trans) => newRpc (trans, st, Error) | EApp ((ECApp ((ENamed n, _), ran), _), trans) => if IS.member (rpcBaseIds, n) then - newRpc (trans, st) + newRpc (trans, st, None) + else if IS.member (trpcBaseIds, n) then + newRpc (trans, st, Error) else (e, st)
--- a/src/settings.sig Mon Apr 01 10:13:49 2013 -0400 +++ b/src/settings.sig Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2011, Adam Chlipala +(* Copyright (c) 2008-2011, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without
--- a/src/shake.sml Mon Apr 01 10:13:49 2013 -0400 +++ b/src/shake.sml Tue Apr 16 10:55:48 2013 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2010, Adam Chlipala +(* Copyright (c) 2008-2010, 2013, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -186,7 +186,7 @@ in case e of ENamed n => check n - | EServerCall (n, _, _) => check n + | EServerCall (n, _, _, _) => check n | _ => s end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tryRpc.ur Tue Apr 16 10:55:48 2013 -0400 @@ -0,0 +1,46 @@ +fun isBeppo (s : string) : transaction string = + case s of + "Beppo" => return "Yup, that's him!" + | "Mephisto" => error <xml>Great googely moogely!</xml> + | _ => return "Who's that?" + +fun listOf (n : int) = + if n < 0 then + error <xml>Negative!</xml> + else if n = 0 then + return [] + else + ls <- listOf (n - 1); + return (n :: ls) + +fun length ls = + case ls of + [] => 0 + | _ :: ls' => 1 + length ls' + +fun main () : transaction page = + s <- source ""; + ns <- source ""; + return <xml><body> + <ctextbox source={s}/> + <button value="rpc" onclick={fn _ => v <- get s; + r <- rpc (isBeppo v); + alert r}/> + <button value="tryRpc" onclick={fn _ => v <- get s; + r <- tryRpc (isBeppo v); + case r of + None => alert "Faaaaaailure." + | Some r => alert ("Success: " ^ r)}/> + + <hr/> + + <ctextbox source={ns}/> + <button value="rpc" onclick={fn _ => v <- get ns; + r <- rpc (listOf (readError v)); + alert (show (length r))}/> + <button value="tryRpc" onclick={fn _ => v <- get ns; + r <- tryRpc (listOf (readError v)); + case r of + None => alert "Faaaaaailure." + | Some r => alert ("Success: " ^ show (length r))}/> + </body></xml>