English version
Accueil     À propos     Téléchargement     Ressources     Contactez-nous    

Ce site est rarement mis à jour. Pour les informations les plus récentes, rendez-vous sur le nouveau site OCaml à l'adresse ocaml.org.

Browse thread
ANN: Dynamic binding library
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: 2006-04-11 (09:00)
From: oleg@p...
Subject: ANN: Dynamic binding library

Dynamic binding adds expressiveness; as Luc Moreau emphasized, it is,
when used in moderation, quite useful: for parameterizing a function
deep in the code without changing the interface of all callers; for
propagating the environment information like the current working
directory, printing-depth, etc.  Dynamic binding is inescapable in
mobile code or continuation-based web servers. Dynamic binding, in
some restricted form, is already present in OCaml: exception handlers
are dynamically bound by the 'handle' form. This message announces the
availability of general dynamic binding. This is joint work with
Chung-chieh Shan and Amr Sabry.

Dynamic binding is implemented as a regular library (dependent on the
delimited continuations library announced earlier on this list).  No
changes to the OCaml system and no code transformations are required;
(parts of the) code that do not use dynamic variables incur no
overhead and run at the same speed as before. Here's the interface:

type 'a dynvar
val dnew : unit -> 'a dynvar

   Given a dynvar, a value and a thunk, evaluate the thunk in the dynamic
   environment extended with the binding of the given value to the
   given dynvar
val dlet : 'a dynvar -> 'a -> (unit -> 'w) -> 'w

   Dereferencing: obtain the value associated with the dynvar
   by the closest dynamically-enclosing dlet
val dref : 'a dynvar -> 'a

   Mutation: obtain the current value of the dynvar and change that
   value to the given one. This `mutation' has the effect only
   within the dynamic scope of the latest dlet
val dset : 'a dynvar -> 'a -> 'a

   Given a dynvar and a function, apply the function to the current
   value of the dynvar, and return the result. The application
   is evaluated in the dynamic environment _outside_ of the
   closest dynamically-enclosing dlet.
val dupp : 'a dynvar -> ('a -> 'w) -> 'w

Our dynamic variables are mutable (cf. fluid-variables in many Scheme
systems); mutations however are visible only within the scope of the
dlet statement where they occurred. Here's the simple example

let test12 =
  let p = dnew () in
  dlet p 1 (fun () ->
    let v1 = dref p in
    let v2 = dlet p 2 (fun () ->
      let v3 = dset p 12 in
      let v4 = dref p in
      (v3,v4)) in
    let v5 = dref p in

which yields (1, (2, 12), 1). The function 'dupp' is an extension of
the conventional dynamic binding. It lets us obtain not only the
current binding to the dynamic variable, but any of the previous
bindings as well. In general, we can fold over the execution stack as
if it were a list (please see the 'nub' example in the test code

Because dynamic binding is implemented in terms of delimited
continuations, the two features harmoniously interact. We can use
dynamic variables in shift-based, cooperative threads. At thread
creation time, the user can `designate' some existing dynamic
variables to be private to the thread; the rest are inherited from the
parent. New dynamic variables and new bindings automatically become
thread-private, so the above designation is accomplished with just
dlet.  Mutation and rebinding of private dynamic variables have effect
only in the corresponding thread. Mutations to shared variables are
visible to everyone. Likewise, a re-parameterization of a dynamic
variable in the parent thread is visible in all child threads sharing
that variable. Such partial inheritance of dynamic environment among
threads is an often desired feature; luckily, it is inherent in our
design and thus available `for free'. The end of the test code
vdynvar.ml demonstrates the partial inheritance among the parent and
two cooperatively run threads.

The implementation is surprisingly, short. In particular,

let dref p      = shift p (fun f -> fun v -> f v v)
let dset p newv = shift p (fun f -> fun v -> f v newv)
let dupp p g    = shift p (fun f -> fun y -> f (g y) y)

with dlet taking not much longer.

The test code:
The dependency: