An Implementation of Transparent Migration on Standard Scheme

I present a handy (though somewhat restrictive) way to implement mobile computation a la Telescript on top of standard Scheme. Background. Mobile computation is an efficient and effective approach to distributed programming where a program works by migrating from one host to another. The migration is called transparent if the execution state of the program is preserved before and after the migration. Transparent migration is preferable to non-transparent, because it is easier to use for application programmers. At the same time, however, it is harder to implement for language developers: all existing implementations (to my knowledge) of transparent migration need either a custom runtime system (e.g. [13]) or global source code transformation (e.g. [12]). Our Method. In this presentation, I describe a library to enable transparent migration in standard Scheme. Unlike existing implementations, it requires neither modification of the runtime system nor transformation of the source code. It works by (i) extracting a delimited continuation [5, 6] with control operators (shift and reset), (ii) reifying the delimited continuation—i.e., reconstructing its source code— with type-directed partial evaluation (TDPE) [2, 4], and (iii) evaluating the source code at the remote host (e.g. by invoking ssh). Assuming the function shift for delimitedcontinuation extraction and the operator ↓ for TDPE, the main part of the library looks like: (define (go rhost) (shift (lambda (k) ; extract the delimited continuation (let ([e (↓()→() k)]) ; reconstruct its source code (reval e rhost))))) ; remotely evaluate the source code Note that the delimited continuation k has the type () → (), because the transparent migration operator go works as a side effect. Since TDPE itself uses control operators, the library actually uses layered control operators [8]. For details of (layered) delimited continuations and TDPE, see Appendix A and B, respectively. Example 1. Consider the following program. (In the examples, we use the non-standard procedure system for the sake of convenience.) (define (hellos) ∗Visiting scholar at the University of Pennsylvania. Research fellow of the Japan Society for the Promotion of Science. (begin (reset ; delimits the continuation (display "hello from ") (system "hostname") (go "remotehost") ; migrates to remotehost (display "another hello from ") (system "hostname")) (display "yet another hello from ") (system "hostname"))) It (i) executes the first display and system at the local host, (ii) extracts, reifies, and remotely evaluates the delimited continuation λ . (display("another hello from "); system ("hostname")), and (iii) performs the third display and system at the local host. As a result, the program works as follows. > (load "go.scm") ; load the library > (hellos) hello from localhost another hello from remotehost yet another hello from localhost Example 2. In the following program, (reset (for-each (lambda (host) (go host) (display "hello from ") (system "hostname")) ’("host1" "host2"))) the delimited continuation of the first migration (to host1) is λ . (display("hello from "); system("hostname"); go("host2"); display("hello from "); system("hostname")) while that of the second migration (to host2) is λ . (display ("hello from"); system("hostname")). It therefore yields the following output. hello from host1 hello from host2 Our method thus unfolds all static recursions, as TDPE does. (In general, this may lead to code duplication or nontermination. See the limitations below for details.) Example 3. In the program below, the delimited continuations of the first and second migrations are λ . (go("host2"); display(3 + 4)) and λ . display(3 + 4), respectively, so the output is 7. (reset (let ([add (lambda (x ) (lambda (y) (+ x y)))]) (go "host1") (let ([add-three (add 3)]) (go "host2") (display (add-three 4))))) As this example suggests, our method inlines all static functions as TDPE does. (This may also lead to code duplication.) Limitations. Because of the limitations of TDPE, our method also has many limitations. To name some, • The reification may not terminate if the delimited continuation contains a dynamic recursion, because TDPE does not terminate if the value has no normal form. Furthermore, the source code of the delimited continuation may become large, because TDPE unfolds all static recursions and inlines all static functions. We can mitigate these problems, however, by letting programmers use a special fixed-point operator that does not recurse during reification. • Operators that involve pointers (such as set! , set-car! , and eq? ) may not work after migration, because pointers have no textual representation and cannot be reified. • Higher-order operands of primitive operators (such as apply and call/cc) must have ML-like types, so that the operands can be reified by TDPE. Moreover, in my current implementation, the types need to be given by the programmer. • Programmers must not take the value of a primitive operator itself (like (define add +)), because it must be substituted by set! in TDPE. In general, it does not work when TDPE does not. Conclusion. Despite the limitations above, I believe that our method may be useful for some applications because of its simplicity. In fact, it is so simple that my current prototype is only a few hundred lines long, including (a variant of) Filinski’s implementation of layered control operators [8], (a variant of) Danvy’s implementation of TDPE [4], comments, blank lines, and debugging code. This can be regarded as an achievement of Scheme’s features such as dynamic typing, set! , and call/cc, which are sometimes blamed as a source of its inefficiency.