16.4
The Origin of State
The notion ofstatein a computation—which will be discussed thoroughly in PartXIII—has its origins in the concept of recursion, or self-reference, which, as we have just seen, arises from the concept of recursive types. For example, you may be familiar with the concept of aflip-flopor alatchat the hardware level. These are circuits built from combinational logic elements (typically, nor or nand gates) that have the characteristic that they maintain an alterable state over time. An RS latch, for example, maintains its output at the logical level of zero or one in response to a signal on the R or S inputs, respectively, after a brief settling delay. This behavior is achieved using feedback, which is just a form of self-reference, or recursion: the output of the gate is fed back into its input so as to convey the current state of the gate to the logic that determines its next state.
One way to model an RS latch using recursive types is to make explicit the passage of time in the determination of the current output of the gate as a function of its inputs and its previous outputs. An RS latch is a value of typeτrslgiven by
µt.hX,→bool,Q,→bool,N,→ti.
TheXandQcomponents of the latch represent its current outputs (of which Qrepresents the current state of the latch), and theNcomponent represents the next state of the latch. Ife is of typeτrsl, then we define e@ Xto mean unfold(e)·X, and definee@ Qande@ Nsimilarly. The expressionse@ Xand e@ Qevaluate to the “current” outputs of the latch,e, ande@ Nevaluates to another latch representing the “next” state determined as a function of the “current” state.2
For given values,rands, a new latch is computed from an old latch by the recursive functionrsldefined as follows:
fixrslisλ(o:τrsl) fixthisisersl,
whereerslis given by
fold(hX,→nor(hs,o@ Qi),Q,→nor(hr,o@ Xi),N,→rsl(this)i) 2For simplicity theRandSinputs are fixed, which amounts to requiring that we build a new latch whenever these are changed. It is straightforward to modify the construction so that newRandSinputs may be provided whenever the next state of a latch is computed, allowing for these inputs to change over time.
16.5 Notes 145
andnoris the obvious function defined on the booleans.3 The outputs of the latch are computed as a function of the rands inputs and the ouputs of the previous state of the latch. To get the construction started, we define an initial state of the latch in which the outputs are arbitrarily set tofalse, and whose next state is determined by applyingrslto the initial state:
fixthisis fold(hX,→false,Q,→false,N,→rsl(this)i).
Selection of theNcomponent causes the outputs to be recalculated based on the current outputs. Notice the essential role of self-reference in main- taining the state of the latch.
The foregoing implementation of a latch models time explicitly by pro- viding the N component of the latch to compute the next state from the current one. It is also possible to model time implicitly by treating the latch as atransducerwhose inputs and outputs aresignalsthat change over time. A signal may be represented by a stream of booleans (as described in Chap- ter15or using general recursive types as described earlier in this chapter), in which case a transducer is a stream transformer that computes the suc- cessive elements of the outputs from the successive elements of the inputs by applying a function to them. This implicit formulation is arguably more natural than the explicit one given above, but it nevertheless relies on recur- sive types and self-reference, just as does the implementation given above.
16.5
Notes
The systematic study of recursive types in programming was initiated by
Scott(1976,1982) to provide a mathematical model of the untypedλ-calculus. The derivation of recursion from recursive types is essentially an applica- tion of Scott’s theory to find the interpretation of a fixed point combina- tor in a model of the λ-calculus given by a recursive type. The category- theoretic view of recursive types was developed byWand(1979) andSmyth and Plotkin(1982). Implementing state using self-reference is fundamental to digital logic. Abadi and Cardelli(1996) andCook(2009), among others, explore similar ideas to model objects. The account of signals as streams is inspired by the pioneering work of Kahn (MacQueen,2009).
3It suffices to require thatfold be evaluated lazily to ensure that recursion is well- grounded. This assumption is unnecessary if the next state component is abstracted on theRandSinputs, as suggested earlier.
Part VI
Chapter 17
The Untyped
λ-Calculus
Types are the central organizing principle in the study of programming languages. Yet many languages of practical interest are said to beuntyped. Have we missed something important? The answer is no. The supposed opposition between typed and untyped languages turns out to be illusory. In fact, untyped languages are special cases of typed languages with a sin- gle, pre-determined recursive type. Far from beinguntyped, such languages areuni-typed.
In this chapter we study the premier example of a uni-typed program- ming language, the(untyped)λ-calculus. This formalism was introduced by Church in the 1930’s as a universal language of computable functions. It is distinctive for its austere elegance. Theλ-calculus has but one “feature”, the higher-order function. Everything is a function, 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 λ-calculus it’s functions all the way down.
17.1
Theλ-Calculus
The abstract syntax ofL{λ}is given by the following grammar:
Exp u ::= x x variable
λ(x.u) λ(x)u λ-abstraction ap(u1;u2) u1(u2) application
The statics ofL{λ}is defined by general hypothetical judgments of the form x1 ok, . . . ,xn ok ` u ok, stating thatuis a well-formed expression in- volving the variablesx1, . . . ,xn. (As usual, we omit explicit mention of the