CS 541 Lecture -*- Outline -*- * Felleisen's paper: On the expressive power of Programming Languages Originally in ESOP '90 (LNCS 432, pages 134-151) Revised in Science of Computer Programming, 17(1-3):35-75, Dec. 1991. Warning: e[x/v] in the paper means [v/x]e (subst v for x in e) Advert: to what extent is call-by-value expressible by call-by-name? does call/cc add anything to expressive power of language? does set! add anything? ** An example: compare Scheme without assignment (set!) vs. Scheme with set! *** Is there a difference? A translation idea (one of many) ------------------------------------------ TRANSLATION EXAMPLE from Scheme with set! to Scheme without it ; ** with set! ** (let (... (TransManager (let ((TransCounter 0)) (lambda (TransType) (if (counter? TransType) TransCounter (begin (set! TransCounter (add1 TransCounter)) BODY))))) ...) ... (TransManager t1) REST) ; ** without set! ** (let (... (TransManager (lambda (TransType TransCounter) (if (counter? TransType) TransCounter (begin (let ((TransCounter (add1 TransCounter))) BODY) TransCounter)))) ...) ... (let ((TransCounter (TransManager t1))) REST)) ------------------------------------------ Q: What's the problem with the above translation without set!? Not encapsulating the TransCounter anymore Why is that bad? violates information hiding If have to change program, have to find all uses Rest of program has to "hide" within the let, so this tranlation doesn't preserve top-level structure of original. So Scheme without set! is not as expressive as Scheme with set! informally; can this be made formal? *** The Turing Tarpit ------------------------------------------ THE TURING TARPIT If each PL can compute all computable functions, then can encode interpreter for one in the other ------------------------------------------ e.g., write interpreter for Scheme with set! in Scheme without set!, translate any program in Scheme with set! as interpreter + data (old prog) Q: how to avoid such "cheating"? *** How to avoid the Turing Tarpit? **** Alternative way around turing tarpit: non-turing-complete languages i.e., languages in which one cannot express all functions take out recursion (while, etc.)! left with "easy" programs over built-in data types and ops. e.g., mapcar gives a kind of looping, APL,... idea is implicit in Kapur and Mandayam paper (7th POPL, 1980) view programming langauges as ADTs want to see what can express in either without loops problem: what if want to compare kinds of loops? e.g., repeat-until and while? **** Felleisen's key idea: compare using *homomorpic translation* i.e., restrict kinds of translations allowed arbitrary translation gives Turing tarpit translation: interpreter + code as data is not "structure preserving" only structure preserving translations can be used locally by programmers or coded in macros. To "implement" this idea, we need a signature compare languages with same signature. Subcase of interest: subset of language expressing whole language i.e., when is a feature syntactic sugar? ** What logicians mean by "expressive power" e.g., is Predicate calculus as expressive as FOPC? can say "more things" in FOPC. *** Formal models of logics ------------------------------------------ FORMAL MODELS OF LOGICS def: logic L = (Exp,Wff,Thm) where Exp contains Wff, Wff contains Thm def: L |- t means t in Thm(L) ------------------------------------------ **** Conservative extension ------------------------------------------ CONSERVATIVE EXTENSION def: L is a conservative extension of L' iff Exp(L) contains Exp(L') Fm(L) intersect Exp(L') = Fm(L') Thm(L) intersect Exp(L') = Thm(L') ------------------------------------------ e.g., arithmetic with add2: N -> N defined by (add2 x) = (succ(succ x)) is a conservative extension of arithmetic **** Definitional extension (syntactic sugar) ------------------------------------------ DEFINITIONAL EXTENSION (SUGARS) def: L is a definitional extension of L' iff there is a homomorphic map Phi: Exp(L) -> Exp(L') such that 1. Phi(Fm(L)) a subset of Fm(L') 2. Phi is identity on Fm(L') 3. Phi is homomorphic in logical ops i.e., for o in {/\, \/, not, ...}, Phi(o(e1,e2)) = o(Phi(e1),Phi(e2)) 4. L |- t iff L' |- Phi(t) 5. L |- (t <==> Phi(t)) def: if L is a definitional extension of L', then the operators in L that are not in L' are eliminable. e.g., Phi(add2 x) = (succ (succ x)) so add2 is eliminable ------------------------------------------ condition 2 means Phi is a "retract" *** Formal Theory of Programming Language Expressiveness **** Languages, conservative extension and restriction ***** Formal programming language, L ------------------------------------------ FORMAL MODEL OF PROGRAMMING LANGUAGE def (3.1): a programming langauge L is (L-Phrases, L-Programs, Eval_L) where L-Phrases contains L-Programs L-Programs contains Eval_L and L-Programs is recursive Eval_L is recursively-enumerable think of Eval_L(P) as "P terminates" ------------------------------------------ L-Phrases are abstract syntax trees, based on function symbols. these Fi are called constructs or facilities. ***** distinguish programs based on whether they terminate or not observation is: run it and see if it halts. Is this enough? Why not look at the output? if output is functional, then can embed program in a context that tests for the result and diverges... (let ((output [ ... ])) (if (= 3 output) STOP DIVERGE)) ***** Conservative extension and restriction ------------------------------------------ CONSERVATIVE EXTENSION AND RESTRICTION Def (3.2): L is a conservative extension of L' iff - L has more constructors, {..., Fn, ...} than L' - all L-Phrases not containing the {..., Fn, ...} are L'-Phrases - L-Programs intersect (L'-Phrases) = L'-Programs - for all L'-Programs P', Eval_L'(P') = Eval_L(P') def: If L is a conservative extension of L', then L' is a conservative restriction of L notation: L + {..., Fn,...} is conservative extension of L notation: L \ {..., Fn,...} is conservative restriction of L ------------------------------------------ e.g., call-by-name lambda calculus is a conservative restriction of call-by-value and call-by-name lambda calculus **** Expressibility (definitional extension, syntactic sugar) ***** Homomorphic translation into L' ------------------------------------------ HOMOMORPHIC TRANSLATION Phi: L'+{...Fn...} -> L' is homomorphic iff for all F in L' (not the Fn) Phi(F(e1, ..., em)) = F(Phi(e1),...,Phi(em)) ------------------------------------------ e.g., Phi(let x be e in e') = (\_v x. Phi(e')) Phi(e) counterexample: interpreter + data translation is not homomorphic. Q: So what does this mean in words? ***** Expressible Programming Constructs ------------------------------------------ EXPRESSIBLE PROGRAMMING CONSTRUCTS Def (3.3): Let L'+{...Fn...} = L. Then the ...Fn... are eliminable from L' iff there is a recursive mapping Phi: L -> L' such that: 1. Phi(L-Program) is a subset of L'-Program 2. Phi is homomorphic into L' 3. for all L-Programs P, eval_L(P) iff eval_L'(Phi(P)) Also say that L' can express the facilities ...Fn... with respect to L. Weak extensibility: instead of condition 3 above use 3'. for all L-Programs P, eval_L(P) implies eval_L'(Phi(P)) ------------------------------------------ Condition 3 ensures that the meaning of programs is preserved. really want Phi to preserve both structure and meaning. *** Describing expressibility using observational equivalence **** contexts (definition 3.4) ------------------------------------------ EXPRESSIBILITY VIA OBSERVATIONAL EQUIVALENCE Contexts: A context C(a) is a term with a "hole" a def: C(a) is an L-program context for e iff C(a) is a context and C(e) is an L-program ------------------------------------------ e.g., C_0(a) = (\_n x y . a)(\_n x .x)Omega_n is a program context for all expressions e such that FV(e) subset of {x,y} **** operational approximation ------------------------------------------ OPERATIONAL APPROXIMATION def: e1 [~_L e2 iff for all program contexts for e1 and e2, C(a), if eval_L(C(e1)) then eval_L(C(e2)) ------------------------------------------ "e1 terminates in no more contexts than e2" "termination of e1 implies termination of e2" Q: What is an example? **** operational (or observational) equivalence ------------------------------------------ OPERATIONAL EQUIVALENCE def: e1 =~_L e2 iff e1 [~_L e2 and e2 [~_L e1 ------------------------------------------ "terminate in same contexts" "no context distinguishes them" **** theorem 3.6, proving constructs expressible ------------------------------------------ PROVING CONSTRUCTS EXPRESSIBLE Thm (3.6): Let L = L' + {...Fn...} be a conservative extension of L'. If Phi: L -> L' is homomorphic and if for all the Fn, Fn(e1,...em) =~_L Phi(Fn(e1,...em)), then L' can express the facilities {...Fn...}. ------------------------------------------ The hypothesis is strong enough to guarantee the translation preserves the semantics of L. Example: let is expressible. The converse does not hold. **** theorem 3.8, proving constructs not expressible ------------------------------------------ PROVING CONSTRUCTS NOT EXPRESSIBLE Thm (3.8): Let L = L' + {...Fn...} be a conservative extension of L'. If all homorphisms into L' do not preserve operational equivalence in L, and if there is a context C(a) over L' that witnesses the inequality, then L' cannot express the facilities {...Fn...}. ------------------------------------------ *** Example application Consider Lambda of Figure 1, language with \_n and \_v Programs is set of closed terms Signature variables are constants (0-ary constructors) x: -> term 2 families of unary constructurs: (one for each var) \_v x: term -> term \_n x: term -> term application operator . : term x term -> term Phrases of Lambda freely generated by terms of this signature Operational semantics: reduction to "head B-normal form" (?) ------------------------------------------ TERMINAL TRANSITION SEMANTICS FOR Lambda Gamma = configurations = closed terms I = identity function on terms T = set of values (v), lambda abstractions O = eval_Lambda ;note below that v must be a lambda (\_n x . e) e' ==> e[x/e'] (\_v x . e) v ==> e[x/v] e0 ==> e0' ____________________ (e0 e1) ==> (e0' e1) e1 ==> e1' ______________________________________ ((\_v x . e0) e1) ==> (\_v x . e0) e1' ------------------------------------------ Proposition 3.9: Lambda_n cannot express \_v with respect to Lambda Both Lambda_n and Lambda_v are turing-complete, but the theory can distinguish them. Rest is research. See paper for more details and more research