Programming languages - application and interpretation
Programming languages - application and interpretation
复制标题
编程语言 - 应用和解释
DOI:
--
复制
发表时间:
2003
期刊:
影响因子:
--
通讯作者:
S. Krishnamurthi
中科院分区:
文献类型:
--
作者:
S. Krishnamurthi
ion has, however, been compromised by the aliasing, and accidental side-effects can leak into the calling contexts, exposing unnecessary implementation details of the procedure. In the early days of programming language design, before programs were particularly sophisticated, the ability to write simple abstractions such as swap was considered valuable (since it is used, for instance, in the implementation of some sorting algorithms). Today, however, we recognize that such abstractions are rather meager in the face of the needs of modern systems. We pay greater attention, instead, to the need for creating useful abstraction boundaries between units of modularity such as procedures: the fewer hidden interactions they have, and the less they interfere with one another, the more easily we can reason about their behavior in isolation. Exercise 14.3.1 While call-by-value preserves the value of variables in the calling context, it does not protect all values. In particular, in many call-by-value languages, a composite data structure (such as a vector) passed as an argument may be mutated by the callee, with the effects visible to the caller. 1. Does this behavior contradict the claim that the language is passing “values” as arguments? Use our investigation of mutable data structures in Section 13 to make your argument rigorous. Hint: Implement an interpreter for a language with both boxes and call-by-reference application, then argue about similarities and differences. 2. Languages like ML tackle this problem by forcing programmers to annotate all mutable data structures using references, the ML counterpart to boxes. Any data structure not so mutated is considered immutable. What trade-offs does ML’s design introduce? Exercise 14.3.2 There appears to be a neutral ground between call-by-value and call-by-reference. Consider the following proposed syntax: {with {swap {fun {x} {fun {y} {with {z x} {seqn {set x y} {set y z}}}}}} {with {a 3} {with {b 2} {seqn {{swap {ref a}} {ref b}} b}}}} The ref notation is an indicator to the interpreter to pass the variable’s location rather than its value; that is, by using {ref a} and {ref b}, the invoker of the procedure indicates his willingness to have his variables be aliased and thus, potentially, be mutated. 1. Modify the interpreter to support the use of ref for procedure arguments. 2. Does this proposal result in a procedural abstraction of the process of swapping the values of two variables? If it does, this would reconcile the design tension between the two invocation techniques: it avoids the difficulty of call-by-value (the inability to write a swap procedure) as well as that of call-by-reference (aliasing of parameters without the caller’s knowledge). Discuss. 140 CHAPTER 14. VARIABLES 3. Suppose programmers are allowed to apply ref to variables elsewhere in the program. What type should the interpreter use to represent the resulting value? How does this compare to an l-value? Does this introduce the need for additional operators in the language? How does this relate to the & operator in C? 14.3. PERSPECTIVE 141 (define-type VCFAE-Value [numV (n number?)] [closureV (param symbol?) (body VCFAE?) (env Env?)]) ;; interp : VCFAE Env Store→ Value×Store (define (interp expr env store) (type-case VCFAE expr [num (n) (v×s (numV n) store)] [add (l r) (type-case Value×Store (interp l env store) [v×s (l-value l-store) (type-case Value×Store (interp r env l-store) [v×s (r-value r-store) (v×s (num+ l-value r-value) r-store)])])] [id (v) (v×s (store-lookup (env-lookup v env) store) store)] [fun (bound-id bound-body) (v×s (closureV bound-id bound-body env) store)] .. Figure 14.1: Implementing Variables, Part 1 142 CHAPTER 14. VARIABLES .. [app (fun-expr arg-expr) (type-case Value×Store (interp fun-expr env store) [v×s (fun-value fun-store) (type-case Value×Store (interp arg-expr env fun-store) [v×s (arg-value arg-store) (local ([define new-loc (next-location arg-store)]) (interp (closureV-body fun-value) (aSub (closureV-param fun-value) new-loc (closureV-env fun-value)) (aSto new-loc arg-value arg-store)))])])] [if0 (test truth falsity) (type-case Value×Store (interp test env store) [v×s (test-value test-store) (if (num-zero? test-value) (interp truth env test-store) (interp falsity env test-store))])] [set (var value) (type-case Value×Store (interp value env store) [v×s (value-value value-store) (local ([define the-loc (env-lookup var env)]) (v×s value-value (aSto the-loc value-value value-store)))])] [seqn (e1 e2) (type-case Value×Store (interp e1 env store) [v×s (e1-value e1-store) (interp e2 env e1-store)])])) Figure 14.2: Implementing Variables, Part 2 14.3. PERSPECTIVE 143 (define-type RVCFAE-Value [numV (n number?)] [closureV (param symbol?) (body RVCFAE?) (env Env?)] [refclosV (param symbol?) (body RVCFAE?) (env Env?)]) ;; interp : RVCFAE Env Store→ Value×Store (define (interp expr env store) (type-case RVCFAE expr [num (n) (v×s (numV n) store)] [add (l r) (type-case Value×Store (interp l env store) [v×s (l-value l-store) (type-case Value×Store (interp r env l-store) [v×s (r-value r-store) (v×s (num+ l-value r-value) r-store)])])] [id (v) (v×s (store-lookup (env-lookup v env) store) store)] [if0 (test pass fail) (type-case Value×Store (interp test env store) [v×s (test-value test-store) (if (num-zero? test-value) (interp pass env test-store) (interp fail env test-store))])] [fun (bound-id bound-body) (v×s (closureV bound-id bound-body env) store)] [refun (bound-id bound-body) (v×s (refclosV bound-id bound-body env) store)] .. Figure 14.3: Implementing Call-by-Reference, Part 1 144 CHAPTER 14. VARIABLES .. [app (fun-expr arg-expr) (type-case Value×Store (interp fun-expr env store) [v×s (fun-value fun-store) (type-case RVCFAE-Value fun-value [closureV (cl-param cl-body cl-env) (type-case Value×Store (interp arg-expr env fun-store) [v×s (arg-value arg-store) (local ([define new-loc (next-location arg-store)]) (interp cl-body (aSub cl-param new-loc cl-env) (aSto new-loc arg-value arg-store)))])] [refclosV (cl-param cl-body cl-env) (local ([define arg-loc (env-lookup (id-name arg-expr) env)]) (interp cl-body (aSub cl-param arg-loc cl-env) fun-store))] [numV ( ) (error ’interp ”trying to apply a number”)])])] [set (var value) (type-case Value×Store (interp value env store) [v×s (value-value value-store) (local ([define the-loc (env-lookup var env)]) (v×s value-value (aSto the-loc value-value value-store)))])] [seqn (e1 e2) (type-case Value×Store (interp e1 env store) [v×s (e1-value e1-store) (interp e2 env e1-store)])])) Figure 14.4: Implementing Call-by-Reference, Part 2