diff --git a/src/Modifier.ml b/src/Modifier.ml index 04fddefc..718cda05 100644 --- a/src/Modifier.ml +++ b/src/Modifier.ml @@ -65,6 +65,11 @@ struct | _ -> None let run f = try_with f () {effc = handler} - let try_with = run + end + + module TryWith (H : Handler) = + struct + module R = Run (H) + let try_with = R.run end end diff --git a/src/ModifierSigs.ml b/src/ModifierSigs.ml index 03ad5e4f..7e304355 100644 --- a/src/ModifierSigs.ml +++ b/src/ModifierSigs.ml @@ -50,17 +50,21 @@ sig sig val run : (unit -> 'a) -> 'a (** [run f h] initializes the engine and runs the thunk [f], using [h] to handle modifier effects. See {!module-type:Handler}. *) + end + module TryWith (H : Handler) : + sig val try_with : (unit -> 'a) -> 'a (** [try_with f h] runs the thunk [f], using [h] to handle the intercepted modifier effects. See {!module-type:Handler}. - Currently, [try_with] is an alias of {!val:run}, but [try_with] is intended to use within {!val:run} to intercept effects, - while {!val:run} is intended to be at the outermost layer to handle effects. That is, the following is the expected program structure: + Currently, [try_with] is an alias of {!val:Run.run}, but [try_with] is intended to use within {!val:Run.run} to intercept or reperform effects, while {!val:Run.run} is intended to be at the top-level to set up the environment and handle effects by itself. That is, the following is the expected program structure: {[ run @@ fun () -> (* code *) - try_with f + try_with @@ fun () -> (* more code *) + try_with @@ fun () -> + (* even more code *) ]} *) end diff --git a/src/Scope.ml b/src/Scope.ml index c219ee89..fc01bd0f 100644 --- a/src/Scope.ml +++ b/src/Scope.ml @@ -89,8 +89,9 @@ struct module M = Mod.Run (H) let run ?(export_prefix=Emp) ?(init_visible=Trie.empty) f = M.run (fun () -> Internal.run ~export_prefix ~init_visible f) - let try_with = M.try_with end + module TryWith (H : Handler) = Mod.TryWith (H) + module Perform = Mod.Perform end diff --git a/src/ScopeSigs.ml b/src/ScopeSigs.ml index 87a021d9..f393ee38 100644 --- a/src/ScopeSigs.ml +++ b/src/ScopeSigs.ml @@ -100,11 +100,12 @@ sig originating from export namespaces. The default is the empty path ([Emp]). This does not affect paths originating from visible namespaces. @param init_visible The initial visible namespace. The default is the empty trie. *) + end + module TryWith (H : Handler) : + sig val try_with : (unit -> 'a) -> 'a - (** Execute the code and handles the internal modifier effects. This can be used to intercept - or reperform those effects; for example, the following function silences the [shadow] effects. - See also {!val:Modifier.S.Run.try_with}. + (** Execute the code and handles the internal modifier effects. [try_with] is intended to use within {!val:Run.run} to intercept or reperform internal effects, while {!val:Run.run} is intended to be at the top-level to set up the environment and handle all effects by itself. For example, the following function silences the [shadow] effects. See also {!val:Modifier.S.TryWith.try_with}. {[ module H = @@ -113,10 +114,10 @@ sig let shadow _ _ _ y = y end - let silence_shadow f = let module R = Run (H) in R.try_with f + let silence_shadow f = let module R = TryWith (H) in R.try_with f ]} - Note that {!val:run} starts a fresh empty scope while [try_with] remains in the current scope. + Note that {!val:Run.run} starts a fresh empty scope while [try_with] remains in the current scope. *) end diff --git a/test/Example.ml b/test/Example.ml index aebc9e93..ab41de84 100644 --- a/test/Example.ml +++ b/test/Example.ml @@ -76,7 +76,7 @@ struct input end -module SilentH : S.Handler = +module SilenceShadow : S.Handler = struct include S.Perform let shadow _ _ _ y = y @@ -84,8 +84,8 @@ end (* Mute the [shadow] effects. *) let silence_shadow f = - let module R = S.Run (SilentH) in - R.try_with f + let module T = S.TryWith (SilenceShadow) in + T.try_with f (* The interpreter *) let rec interpret_decl : decl -> unit =