Reference Dynamically Typed Programming Languages Part 1: The Untyped λ-calculus Jim Royer CIS 352 April 19, 2018 Practical Foundations for Programming Languages, 2/e, Part VI: Dynamic Types, by Robert Harper, Cambridge University Press, 2016, pages 183 210. https://www.cs.cmu.edu/%7erwh/pfpl/2nded.pdf Note: Harper doesn t much care for dynamically typed languages and his criticisms are dead on, but these languages do have some advantages. Lambda calculus definition, from Wikipedia. https://en.wikipedia.org/wiki/lambda_calculus_definition Royer Dynamically Typed Programming Languages 1 / 19 Royer Dynamically Typed Programming Languages 2 / 19 Important!!! dynamically typed dynamically scoped Dynamically typed roughly means we may not find out the type of a value until runtime. The λ-calculus Dynamically scoped roughly means we may not find out a variable s binding until runtime. Dynamically Typed Programming Languages include: Lisp, Scheme, Racket, Python, Dylan,... The premier example is the (untyped) λ-calculus. Royer Dynamically Typed Programming Languages 3 / 19 Royer Dynamically Typed Programming Languages 4 / 19
The λ-calculus: Beginnings The λ-calculus: Beginnings The λ-calculus: Beginnings The λ-calculus: Beginnings The formalism was cut back to the part that was about defining functions, λ-calculus. Amazingly... The formalism was cut back to the part that was about defining functions, λ-calculus. Amazingly... The λ-calculus turns out to be Turing-complete. Why amazingly?
The λ-calculus: Beginnings Definitions, 1 The formalism was cut back to the part that was about defining functions, λ-calculus. Amazingly... The λ-calculus turns out to be Turing-complete. Why amazingly? Because on the surface there is not very much to the λ-calculus. Concrete Syntax Note: E ::= X (variable occurrence) (E E) (function application) λx. E (function abstraction) X ::= identifiers We sometimes add extra parens around lambda-expressions. E.g., we can write ((λx.(y x)) z) for (λx.(y x) z). Also, by convention application associates to the left. E.g., we can write w x y z for (((w x) y) z) as in Haskell. Harper uses a LISPy concrete syntax for λx.e. E.g.: λ(x)e Royer Dynamically Typed Programming Languages 6 / 19 Free and bound variables Abstract Syntax (in Haskell) E ::= X (E E) λx.e type Name = String data Exp = Id Name App Exp Exp Lam Name Exp ((λ I x.( B x F z)) F x) I = binding occurrence B = bound occurence F = free occurence Id x Lam x App App Id z Id x Computations Definition An expression of the form: ((λx.e 0 ) e 1 ) is called a β-redex. ((λx.e 0 ) e 1 ) β-reduces to e 0 [e 1 /x]. e β-reduces to e when e is the result of replacing some subexpression of e of the form ((λx.e 0 ) e 1 ) with e 0 [e 1 /x]. e is in β-normal form when it contains no β-redexes. Examples ((λx.(plus x z)) y) β (plus y z) ((λw.(plus w z)) (times x y)) β (plus (times x y) z) ((λx.(λz.(plus x z))) y) β (λz.(plus y z)) ((λx.(λy.(plus x y))) y) β (λy.(plus y y)) variable capture!! ((λx.(λy.(plus x y))) y) β (λz.(plus y z)) (x (λy.y)) β anything as it is in normal form Ω = def ((λx.(x x)) (λx.(x x))) β ((λx.(x x)) (λx.(x x))) = Ω Royer Dynamically Typed Programming Languages 7 / 19 Royer Dynamically Typed Programming Languages 8 / 19
β-reduction s less glamorous siblings Normal Order Evaluation (think preorder) α-conversion (Bound vars don t mater for meaning) λx.e α λy.(e[y/x]) where x = y. (I.e., Renaming bound vars doesn t change meaning.) η-conversion (Extensionality) ((λx.e) x) η e when x / freevars(e). (I.e., λ-terms producing the same result on all args are equivalent.) The Normal Order Evaluation Strategy Always do the leftmost possible beta-reduction. Repeat until (if ever) you reach a normal form. Normal Order Evaluation Strategy is equivalent to: function nor(m) if M = ((λx.n) P) then nor(n[p/x]) else if M = (N P) & N n.o. N then nor( (N P) ) else if M = (N P) & P n.o. P then nor( (N P ) ) else if M = λx.n & N n.o. N then nor(λx.n ) ( ) else (* M is in β-n.f. *) return M [Draw the parse tree!] Theorem If e has a normal form, normal order evaluation will eventually reach it. ( ) Drop this line to get to call-by-name. Royer Dynamically Typed Programming Languages 9 / 19 Royer Dynamically Typed Programming Languages 10 / 19 Applicative Order Evaluation (think postorder) The Applicative Order Evaluation Strategy Always do the innermost (to the left) possible beta-reduction. Repeat until (if ever) you run out of beta-redexes. [Draw the parse tree!] Normal Order Evaluation Strategy is equivalent to: function nor(m) if M = (N P) & N n.o. N then nor( (N P) ) else if M = (N P) & P n.o. P then nor( (N P ) ) else if M = ((λx.n) P) then nor(n[p/x]) else if M = λx.n & N n.o. N then nor(λx.n ) ( ) else (* M is in β-n.f. *) return M 2018-04-19 Dynamically Typed Programming Languages Applicative Order Evaluation (think postorder) Are there other evaluation strategies? More than you want to know. For starts, see: https://en.wikipedia.org/wiki/evaluation_strategy. Applicative Order Evaluation (think postorder) 11 / 19[width=8cm] The Applicative Order Evaluation Strategy Always do the innermost (to the left) possible beta-reduction. Repeat until (if ever) you run out of beta-redexes. [Draw the parse tree!] Normal Order Evaluation Strategy is equivalent to: function nor(m) if M = (N P) & N n.o. N then nor( (N P) ) else M = (N P) & P n.o. P then nor( (N P ) ) if else if M = ((λx.n) P) then nor(n[p/x]) then nor(λx.n ) else if M = λx.n & N n.o. N ( ) else (* M is in β-n.f. *) return M Fact If e has a normal form, applicative order evaluation may not find it. ( ) Drop this line to get to call-by-value. Fact If e has a normal form, applicative order evaluation may not find it. ( ) Drop this line to get to call-by-value. Royer Dynamically Typed Programming Languages 11 / 19
The λ-calculus as a RISC assembly language, 1 The λ-calculus as a RISC assembly language, 2 Church Booleans true = def λt.λf.t false = def λt.λf.f test = def λb.λm.λn.((l m) n) and = def λb.λc.((b c) false) Examples test true u v β u test false u v β v and true true β true and false true β false. Church Pairs pair = def λf.λs.λb.((b f ) s) fst = def λp.(p true) snd = def λp.(p false) Examples fst (pair u v) β u snd (pair u v) β u Church Numerals c 0 = def λs.λz.z c 1 = def λs.λz.(s z) c 2 = def λs.λz.(s (s z)) c 3 = def λs.λz.(s (s (s z))). successor = def λn.λs.λz.(s (n s z)) plus = def λm.λn.λs.λz.m s (n s z) times = def λm.λn.(m (plus n) c 0 ). Y = def λf.(λx.f (x x))(λx.f (x x)) Object oriented integers specify what successor (s) is specify what zero (z) is c n = apply s n-times to z predecessor is difficult (pred c 0 β c 0 & pred c n+1 β c n ) Royer Dynamically Typed Programming Languages 12 / 19 Royer Dynamically Typed Programming Languages 13 / 19 From Barendregt s Impact of the Lambda calculus Kleene did find a way to lambda define the predecessor function in the untyped lambda calculus, by using an appropriate data type (pairs of integers) as auxiliary device. In [69], he described how he found the solution while being anesthetized by laughing gas (N 2 O) for the removal of four wisdom teeth. Back to Harper https://nsl.cs.usc.edu/~jkna/fpl/church.pdf Royer Dynamically Typed Programming Languages 14 / 19 Royer Dynamically Typed Programming Languages 15 / 19
Term formation à la Harper Term equivalence à la Harper There is just one type: ok. Γ, x : ok x : ok Γ, x : ok e : ok Γ λx.e : ok Γ u u asserts that u and u (with free variables in Γ), think u and u are behaviorally equivalent. Γ, u : ok u u Γ u u Γ u u Γ u u Γ u u Γ u u Γ u 1 u 1 Γ u 2 u 2 Γ (u 1 u 2 ) (u 1 u 2 ) Γ, x : ok u u Γ λx.u λx.u Γ e 1 : ok Γ e 2 : ok Γ (e 1 e 2 ) : ok Γ, x : ok u 2 : ok Γ e 1 : ok Γ ((λx.e 2 ) e 1 ) e 2 [e 1 /x] Scott s Theorem u u is undecidable. So, if there was any doubt, we are firmly in Turing s tar-pit. Royer Dynamically Typed Programming Languages 16 / 19 Royer Dynamically Typed Programming Languages 17 / 19 2018-04-19 Dynamically Typed Programming Languages Term equivalence à la Harper The first three rules say that is an equiv. rel. Term equivalence à la Harper 17 / 19[width=8cm] Γ u u asserts that u and u (with free variables in Γ), think u and u are behaviorally equivalent. Γ, u : ok u u Scott s Theorem Γ u u Γ u u Γ u u Γ u u Γ u u Γ u1 u 1 Γ u2 u 2 Γ, x : ok u u Γ (u1 u2) (u 1 u 2 ) Γ λx.u λx.u u u is undecidable. Γ, x : ok u2 : ok Γ e1 : ok Γ ((λx.e2) e1) e2[e1/x] The fourth rule states that applying things, yields results. So, if there was any doubt, we are firmly in Turing s tar-pit. The fifth rule states that abstracting on things, yields results. The last rule says that u β u implies u u. I.e., an evaluation step (β-reduction) preserves meaning. Observation: The λ-calculus is seriously strange The λ-calculus has but one feature, the higher-order function. Everything is a function, and hence every expression may be applied to an argument, which must itself be a function, with the result also being a function. To borrow a turn of phrase, in the λ-calculs it s functions all the way down. Harper, page 127 In terms of the type ok, we have: ok = (ok ok) By Cantor s Theorem, the above makes no sense in standard set theory. Royer Dynamically Typed Programming Languages 18 / 19
Observation: The λ-calculus is seriously strange The λ-calculus has but one feature, the higher-order function. Everything is a function, and hence every expression may be applied to an argument, which must itself be a function, with the result also being a function. To borrow a turn of phrase, in the λ-calculs it s functions all the way down. Harper, page 127 2018-04-19 Dynamically Typed Programming Languages Observation: The λ-calculus is seriously strange By Cantor, if card(a) > 1, then where card(a) < card(a A ) A A = the set of (total) functions from A to A. Observation: The λ-calculus is seriously strange 18 / 19[width=8cm] The λ-calculus has but one feature, the higher-order function. Everything is a function, and hence every expression may be applied to an argument, which must itself be a function, with the result also being a function. To borrow a turn of phrase, in the λ-calculs it s functions all the way down. Harper, page 127 In terms of the type ok, we have: ok = (ok ok) By Cantor s Theorem, the above makes no sense in standard set theory. However, it turns out to make sense in a computability context. In terms of the type ok, we have: ok = (ok ok) By Cantor s Theorem, the above makes no sense in standard set theory. However, it turns out to make sense in a computability context. Royer Dynamically Typed Programming Languages 18 / 19 How to make computational sense of ok = (ok ok) Briefly, as a recursive type. E.g., in Haskell data Ok = F (Ok -> Ok) We are skipping Harper s treatment of recursive types, but: It isn t too hard to represent the λ-calculus using a type like Ok. But it also is a pain. (See Harper.)... and it breaks as soon as you add any other data type to the lambda calculus, which is exactly what we want to do. So there is another approach. (Next time!) Royer Dynamically Typed Programming Languages 19 / 19