• No results found

2. IfA0 u, then w(puq)≡0. 3. IfA1 u, then w(puq)≡1.

Proof. Suppose there is such an untyped termw. Letvbe the untyped term λ(x) ifz(w(x);u1; .u0), whereA0u0andA1u1. By Lemma17.1there is

an untyped termtsuch thatv(ptq)≡ t. Ifw(ptq)≡ 0, thent ≡ v(ptq)≡ u1, and soA1 t, because A1 is behavioral andA1 u1. But thenw(ptq) ≡

1 by the defining properties of w, which is a contradiction. Similarly, if w(ptq)≡1, thenA0 t, and hencew(ptq)≡0, again a contradiction.

Corollary 17.3. There is no algorithm to decide whether or not u≡u0.

Proof. For fixed u, the property Eu u0 defined by u0 ≡ u is a non-trivial behavioral property of untyped terms. It is therefore inseparable from its negation, and hence is undecidable.

17.4

Untyped Means Uni-Typed

The untyped λ-calculus may be faithfully embedded in a typed language with recursive types. This means that every untypedλ-term has a represen- tation as a typed expression in such a way that execution of the representa- tion of aλ-term corresponds to execution of the term itself. This embedding is nota matter of writing an interpreter for the λ-calculus in L{+×*µ} (which we could surely do), but rather a direct representation of untyped λ-terms as typed expressions in a language with recursive types.

The key observation is that theuntypedλ-calculus is really theuni-typed λ-calculus. It is not theabsenceof types that gives it its power, but rather that it hasonly onetype, namely the recursive type

D,µt.t→t.

A value of typeDis of the formfold(e)whereeis a value of typeD→D — a function whose domain and range are bothD. Any such function can be regarded as a value of typeDby “rolling”, and any value of typeDcan be turned into a function by “unrolling”. As usual, a recursive type may be seen as a solution to a type isomorphism equation, which in the present case is the equation

D∼=D→D.

This specifies that Dis a type that is isomorphic to the space of functions on Ditself, something that is impossible in conventional set theory, but is feasible in the computationally-based setting of theλ-calculus.

156 17.5 Notes

This isomorphism leads to the following translation, ofL{λ}intoL{+×*µ}:

x†,x (17.14a)

λ(x)u†,fold(λ(x:D)u†) (17.14b) u1(u2)†,unfold(u1†)(u†2) (17.14c)

Observe that the embedding of aλ-abstraction is a value, and that the embedding of an application exposes the function being applied by un- rolling the recursive type. Consequently,

λ(x)u1(u2)† =unfold(fold(λ(x:D)u†1))(u†2)

λ(x:D)u†1(u†2)

≡[u†2/x]u†1 = ([u2/x]u1)†.

The last step, stating that the embedding commutes with substitution, is easily proved by induction on the structure ofu1. Thusβ-reduction is faith- fully implemented by evaluation of the embedded terms.

Thus we see that the canonicaluntypedlanguage,L{λ}, which by dint of terminology stands in opposition to typed languages, turns out to be but a typed language after all. Rather than eliminating types, an untyped language consolidates an infinite collection of types into a single recursive type. Doing so renders static type checking trivial, at the expense of incur- ring substantial dynamic overhead to coerce values to and from the recur- sive type. In Chapter18we will take this a step further by admitting many different types of data values (not just functions), each of which is a com- ponent of a “master” recursive type. This shows that so-calleddynamically typedlanguages are, in fact,statically typed. Thus this traditional distinction can hardly be considered an opposition, because dynamic languages are but particular forms of static languages in which undue emphasis is placed on a single recursive type.

17.5

Notes

The untyped λ-calculus was introduced by Church (1941) as a codifica- tion of the informal concept of a computable function. Unlike the well- known machine models, such as the Turing machine or the random access

17.5 Notes 157

machine, the λ-calculus directly codifies mathematical and programming practice. Barendregt(1984) is the definitive reference for all aspects of the untyped λ-calculus; the proof of Scott’s theorem is adapted from Baren- dregt’s account. Scott(1980) gave the first model of the untypedλ-calculus in terms of an elegant theory of recursive types. This construction under- lies Scott’s apt description of theλ-calculus as “unityped”, rather than “un- typed.”

Chapter 18

Dynamic Typing

We saw in Chapter17 that an untyped language may be viewed as a uni- typed language in which the so-called untyped terms are terms of a distin- guished recursive type. In the case of the untypedλ-calculus this recursive type has a particularly simple form, expressing that every term is isomor- phic to a function. Consequently, no run-time errors can occur due to the misuse of a value—the only elimination form is application, and its first ar- gument can only be a function. This property breaks down once more than one class of value is permitted into the language. For example, if we add natural numbers as a primitive concept to the untypedλ-calculus (rather than defining them via Church encodings), then it is possible to incur a run-time error arising from attempting to apply a number to an argument, or to add a function to a number. One school of thought in language design is to turn this vice into a virtue by embracing a model of computation that has multiple classes of value of a single type. Such languages are said to bedynamically typed, in purported opposition tostatically typedlanguages. But the supposed opposition is illusory: just as the so-called untyped λ- calculus turns out to be uni-typed, so dynamic languages turn out to be but restricted forms of static language. This remark is so important it bears repeating: every dynamic language is inherently a static language in which we confine ourselves to a (needlessly) restricted type discipline to ensure safety.

18.1

Dynamically Typed PCF

To illustrate dynamic typing we formulate a dynamically typed version of L{nat*}, called L{dyn}. The abstract syntax of L{dyn}is given by the

160 18.1 Dynamically Typed PCF

following grammar:

Exp d ::= x x variable

num(n) n numeral

zero zero zero

succ(d) succ(d) successor

ifz(d;d0;x.d1) ifzd{zero⇒d0| succ(x)⇒d1}

zero test fun(λ(x)d) λ(x)d abstraction ap(d1;d2) d1(d2) application

fix(x.d) fixxisd recursion

There are two classes of values in L{dyn}, thenumbers, which have the form n, and the functions, which have the form λ(x)d. The expressions zero andsucc(d) are not values, but rather are operations that evaluate to values. General recursion is definable using a fixed point combinator, but is taken as primitive here to simplify the analysis of the dynamics in Section18.3.

As usual, the abstract syntax ofL{dyn}is what matters, but we use the concrete syntax to write examples in a convenient manner. However, it is often the case for dynamic languages, includingL{dyn}, that the concrete syntax is deceptive in that it obscures an important detail of the abstract syntax, namely that every value is tagged with a classifier that plays a sig- nificant role at run-time (as we shall see shortly). So although the concrete syntax for a number,n, suggests a “bare” representation, the abstract syn- tax reveals that the number is labelled with the classnumto indicate that the value is of the numeric class. This is done to distinguish it from a function value, which concretely has the formλ(x)d, but whose abstract syntax, fun(λ(x)d), indicates that it is to be classified with the tagfunto distin- guish it from a number. As we shall see shortly, this tagging is of prime importance in any dynamic language, so it is important to pay close atten- tion to the abstract form in what follows.

The statics of L{dyn}is essentially the same as that of L{λ}given in Chapter17; it merely checks that there are no free variables in the expres- sion. The judgment

x1ok, . . .xnok`dok

states thatd is a well-formed expression with free variables among those in the hypotheses. If the assumptions are empty, then we write justdokto indicate thatdis a closed expression ofL{dyn}.

18.1 Dynamically Typed PCF 161

The dynamics ofL{dyn}must check for errors that would never arise in a language such as L{nat*}. For example, evaluation of a function application must ensure that the value being applied is indeed a function, signaling an error if it is not. Similarly the conditional branch must ensure that its principal argument is a number, signaling an error if it is not. To account for these possibilities, the dynamics is given by several judgment forms, as summarized in the following chart:

dval dis a (closed) value

d 7→d0 devaluates in one step tod0 derr dincurs a run-time error dis numn dis of classnumwith valuen disnt num dis not of classnum

dis funx.d dis of classfunwith bodyx.d disnt fun dis not of classfun

The last four judgment forms implement dynamic class checking. They are only relevant whendhas already been determined to be a value. The affir- mative class-checking judgments have a second argument that represents the underlying structure of a value; this argument isnotitself a value.

The value judgment, d val, states that d is a fully evaluated (closed) expression:

num(n)val (18.1a)

fun(λ(x)d)val (18.1b) The affirmative class-checking judgments are defined by the following rules:

num(n)is numn (18.2a) fun(λ(x)d)is funx.d (18.2b) The negative class-checking judgments are correspondingly defined by these rules:

num( )isnt fun (18.3a)

fun( )isnt num (18.3b)

The transition judgment, d 7→ d0, and the error judgment, d err, are defined simultaneously by the following rules:

zero7→num(z) (18.4a)

d7→ d0

succ(d)7→ succ(d0) (18.4b)

162 18.1 Dynamically Typed PCF derr succ(d)err (18.4c) dis numn succ(d)7→num(s(n)) (18.4d) disnt num

succ(d)err (18.4e)

d 7→d0 ifz(d;d0;x.d1)7→ifz(d0;d0;x.d1) (18.4f) derr ifz(d;d0;x.d1)err (18.4g) dis num0 ifz(d;d0;x.d1)7→d0 (18.4h) dis numn+1

ifz(d;d0;x.d1)7→[num(n)/x]d1 (18.4i)

disnt num ifz(d;d0;x.d1)err (18.4j) d1 7→d01 ap(d1;d2)7→ap(d10;d2) (18.4k) d1err ap(d1;d2)err (18.4l) d1is funx.d ap(d1;d2)7→[d2/x]d (18.4m) d1isnt fun ap(d1;d2)err (18.4n)

fix(x.d)7→[fix(x.d)/x]d (18.4o) Rule (18.4i) labels the predecessor with the classnumto maintain the invari- ant that variables are bound to expressions ofL{dyn}.

Lemma 18.1(Class Checking). If dval, then

1. either dis numn for some n, or disnt num;

2. either dis funx.d0for some x and d0, or disnt fun.

Proof. By a straightforward inspection of the rules defining the class-checking judgments.

18.2 Variations and Extensions 163