Algorithm W Created 2026-10-14 Updated 2026-11-05
Algorithm W infers data types for a pure lambda calculus with let polymorphism. Write , where is a type substitution and the inferred data type. Composition means applying first. The following presentation uses type schemes explicitly.[ref]
For a program identifier, take a fresh instance of its type scheme in the typing context. For , introduce fresh , infer , and return .
For an application , compute and . With fresh , let be the most general unifier of and . Return . An incompatible base data type or failed occurs check rejects the expression.
For let x = e1 in e2, first infer . Generalize exactly those type variables of not free in , producing a type scheme . Infer for under and return .
For example, let id = \x -> x in let a = id 3 in id True has a Boolean data type result. The identity function receives type scheme , then separate uses instantiate it at integer and Boolean data types. A parameter to a lambda abstraction instead has one shared monomorphic type; treating that parameter like a let-bound type scheme would accept programs outside this type system.
Beta-eta equivalence 2026-10-31
The least equality on lambda terms closed under lambda application and lambda abstraction and containing beta equivalence and eta conversion, with bound names identified by alpha equivalence.
Eta conversion Created 2026-10-14 Updated 2026-10-31
Eta conversion expresses extensionality for a lambda abstraction whose fresh argument is only passed to a function. The side condition prevents removal of a genuine dependency. In a categorical model with , the inverse laws for currying and evaluation validate this conversion as well as beta reduction.
Function type 2026-10-13
A function type classifies functions that accept arguments of type and produce results of type if they return. For example, has function type in our teaching programming language, while has function type . A function type specifies how a function may be used; it does not guarantee that a call returns.
Figure 1.
Checking an integer function call from its parts
. An addition rule checks the body of a lambda abstraction, the abstraction rule gives its function type, and lambda application checks the argument and obtains the result type. Arrows run from prerequisite typing judgments to their conclusions. Details.
The two central typing judgment rules are
The lambda abstraction rule checks the body under an extended typing context; the lambda application rule checks that the argument has exactly the required type. These rules describe our simple type examples. [ref]
For , assume ; both and have type int, so has type int. The abstraction therefore has function type . Since has type int, the call has type int and evaluates to . Replacing by true fails the argument requirement. This local derivation explains each step without running the program.
Arrows associate to the right: means , a function returning another function. For instance, has function type . Supplying one integer yields a function awaiting the second. Functions can also be arguments: accepts an integer-to-integer function and has an integer result type.
// Target: computer-science.bigb
Let ordered by subset. Encode a finite subset and output by a natural number . Define and . Every Scott continuous map satisfies , proving that application is the projection of a retraction. Lambda abstraction has graph .
Identity weakening rule Created 2026-10-06 Updated 2026-10-25
The rule with premises and conclusion retains the first derivation while allowing an unused second assumption. Under the Curry-Howard correspondence, the output term is the first premise term, with the enlarged context. This allows a lambda abstraction whose bound variable does not occur in its body.
Implication introduction rule Created 2026-10-06 Updated 2026-10-31
The implication introduction rule discharges a temporary assumption from a derivation of , producing . Under the Curry-Howard correspondence, a body under gives the lambda abstraction . Unused assumptions may also be discharged.
Lambda application 2026-10-14
Application applies the lambda term to the lambda term . It associates to the left: . Together with variables and lambda abstraction, it generates all lambda terms.
Lambda calculus Created 2026-09-28 Updated 2026-11-05
A lambda term is a variable , a lambda application , or a lambda abstraction . The lambda abstraction binds in and represents a function whose argument will be substituted for . Computation uses beta reduction:
where the substitution is capture-avoiding substitution. For example, reduces to , while substituting a free into first requires renaming its bound variable, giving rather than .
The untyped lambda calculus allows self-application. If , then reduces to itself and has no terminating reduction. Conversely, Church numerals encode natural numbers as repeated application: represents two. This small syntax supports general computation while making binding, substitution and termination visible. The simply typed lambda calculus restricts the allowed lambda applications through types.
Lambda model Created 2026-10-14 Updated 2026-10-20
A lambda model interprets variables, lambda application and lambda abstraction so that capture-avoiding substitution agrees with environments and all beta equivalence equations hold. A reflexive domain gives such an interpretation. Validity of eta conversion requires an extra inverse law and does not follow merely from a retraction.
Let polymorphism 2026-10-14
Let polymorphism gives an eligible let-bound name a type scheme, allowing fresh instantiation at each use. In the pure Hindley-Milner type system, generalize the inferred type's type variables that are free in that type and not free in the current typing context. The identity function example under type variable meets this condition. A lambda abstraction's parameter receives one monomorphic type shared by its uses. Languages with mutation, such as Standard ML with ML reference types, need additional restrictions on generalization.
Parallel beta reduction 2026-10-14
Parallel beta reduction permits simultaneous reduction in the body of a lambda abstraction and both sides of an application, together with when and . Variables reduce to themselves. Its capture-avoiding substitution lemma is . Its transitive closure equals ordinary finite beta reduction, while its one-step diamond property yields the Church-Rosser theorem.
Write for the I combinator, and choose outside the free variables of . Both orders of composition of lambda terms first give the same lambda abstraction by beta reduction:
The freshness condition is exactly the side condition for eta conversion, which contracts to . Therefore
Here eta conversion matters: a free variable is already in beta-normal form, as is , and beta equivalence alone does not equate them.
The argument order in is a cyclic permutation. Undo it by rotating in the other direction. For , choose the lambda term
Use fresh bound variables throughout. Substituting for the first argument of and then performing beta reduction gives
Indeed, the arguments received by are , so its last argument moves back to the front. Apply eta conversion successively, starting with the innermost lambda abstraction. At each step the bound variable being removed is absent from the preceding expression. Thus the last expression is related to by beta-eta equivalence, and consequently
For the induction needed in the next part, also compute the reverse composition of lambda terms:
So this is a two-sided inverse. The empty argument list at means ; choose . For , both displayed rotations give , which is also related to by eta conversion. These boundary cases require no exceptional inverse construction.
Choose as the domain of the new function type. Since , the assumption is consistent with and deleting leaves unchanged. The lambda abstraction rule gives
Use the universal typing axiom for , then the lambda application rule:
Therefore
The condition is about the typing context, not the free variables of . Some occurrences of in might have been hidden by a use of the universal typing axiom; the derivation above still follows the printed rules.
Begin with the variable typing judgment . Because is absent from this typing context, the lambda abstraction rule permits to have type :
Assign the entire lambda application type using the universal typing axiom, without attempting to assign a function type to . Then
Although is a free variable of the body, it is absent from its recorded typing context. Consequently the lambda abstraction rule allows any simple type for . Abstract over and then :
The side condition for holds because is absent; the side condition for holds because its recorded simple type is . Thus
The exact restriction in the ordinary simply typed lambda calculus is
Thus the unrestricted choice of available in part (c) fails. A reading of the printed sentence as asserting impossibility for every choice of is false. This issue occurs in the original PDF as well as its transcription.[ref]
To prove necessity, first observe why the ordinary rules cannot hide assumptions. Structural induction on a derivation proves
For a variable, both sides are the singleton containing that variable. For lambda application, both sides are the union of the two premise supports. For lambda abstraction, both sides delete the bound variable. There is no universal typing axiom to interrupt this equality.
Now invert the proposed derivation. Each outer lambda abstraction must have been introduced by the abstraction rule, since the other ordinary rules conclude a variable or a lambda application. The body must therefore have the typing judgment
The support equality just proved ensures that both displayed assumptions occur; consistency fixes their simple types to those specified by the outer function type. Inverting the lambda application gives a simple type for its argument and the following premises:
There is no flexibility in the supports: has the single free variable , whereas has both free variables . Invert the second lambda application. Its two premises must be
for some simple type . The variable rule is the only possible final rule for each premise. It forces and , yielding .
Conversely, suppose . The ordinary rules give
The typing contexts are consistent, so another lambda application gives the body type . Two lambda abstractions discharge and , establishing
This proves sufficiency and supplies a family of counterexamples to the literal impossibility claim. For a concrete failure of the unrestricted claim, choose and as atomic type variables. The type variable is not a function type , so no derivation exists for that fixed pair. Here the atomic type variables are fixed type expressions, rather than unknowns that one may change during type inference.
The reason for the restriction is the otherwise discarded lambda application : the ordinary rules still type-check it. Its subsequent disappearance under beta reduction does not license reversing that beta reduction while preserving every simple type of the result. The beta-normal form admits arbitrary , whereas the unreduced lambda term has the additional constraint just derived.
Use programming closures that receive evaluated arguments and return a computation in the chosen computational monad. A sufficient core of the semantic value domain is:
data Value = IntVal Integer
           | BoolVal Bool
           | Function ([Value] -> M Value)
Additional constructors may represent the other built-in values. Assume a semantic environment abstract data type with operations lookupEnv :: Env -> Ident -> Value and extend :: Env -> [(Ident,Value)] -> Env. The latter simultaneously adds the listed bindings, shadowing old bindings. Parameter names in one binding list are distinct, and calls have the expected arity.
Define programming closure construction separately; it does not run the body:
close :: Expr -> Env -> Value
close (Lambda xs body) rho =
  Function (\args -> eval body (extend rho (zip xs args)))

apply :: Value -> [Value] -> M Value
apply (Function f) args = f args
The four expression forms have the following semantic equations. Function position is evaluated first, followed by argument expressions from left to right:
eval :: Expr -> Env -> M Value

eval (Variable x) rho = result (lookupEnv rho x)

eval (Apply e es) rho =
  bind (eval e rho) (\f ->
    bind (evalArgs es rho) (\args -> apply f args))

eval e@(Lambda xs body) rho = result (close e rho)

eval (Let d body) rho =
  bind (defn d rho) (\rho' -> eval body rho')

evalArgs :: [Expr] -> Env -> M [Value]
evalArgs [] rho = result []
evalArgs (e:es) rho =
  bind (eval e rho) (\v ->
    bind (evalArgs es rho) (\vs -> result (v:vs)))
The two definition forms are:
defn :: Defn -> Env -> M Env

defn (Val x e) rho =
  bind (eval e rho) (\v -> result (extend rho [(x,v)]))

defn (Rec x e@(Lambda xs body)) rho = result rho'
  where
    rho' = extend rho [(x,v)]
    v = close e rho'
The ordinary definition evaluates its right-hand side in the old semantic environment; its own name is not yet bound there. The recursive definition creates a recursive closure in the extended semantic environment containing itself. Because the right-hand side is a lambda abstraction, constructing that programming closure does not evaluate its body. Haskell's lazy evaluation can tie this recursive data structure without requiring any extra fixed-point operation from the arbitrary computational monad.
Lexical scope is explicit: calling a programming closure extends the defining semantic environment, and its parameters shadow bindings in that semantic environment. The six requested constructs are therefore covered by
The equivalence holds for every lawful computational monad with the semantic equations above. In particular, effects in are neither discarded nor evaluated twice. Fix an initial semantic environment , and write
Expanding Let and Val gives
The two steps use associativity and left identity from the monad laws.
For the application, evaluating the lambda abstraction yields result c, where the programming closure satisfies . The singleton argument evaluator reduces to
Use left identity to remove the initial result c, then associativity and left identity again:
Consequently,
The argument relies on the lambda abstraction itself being effect-free, as specified in part (a), and on using the same semantic environment for on each side. No commutativity of effects is assumed. In particular, the proof applies to state monads, exceptions and continuation monads, not merely to the identity monad.
Add Fixes [Ident] [Expr] to the abstract syntax tree. For the stipulated nonempty, equally long lists of distinct names and lambda abstractions, define all programming closures against one common extended semantic environment:
eval (Fixes xs es) rho = result (head vs)
  where
    rho' = extend rho (zip xs vs)
    vs = map (\e -> close e rho') es
Expanding the auxiliary function, if ,
Every body can therefore call every member of the family, including itself. Constructing the family performs no body computations in the arbitrary computational monad. As in part (c), host lazy evaluation ties the recursive semantic environment; a monadic fixed-point operator is unnecessary.
For example, with two bodies implementing evenness and oddness, respectively, each programming closure sees both names in . Returning the first programming closure still retains access to the second. Building each programming closure in an independently extended old semantic environment would lose this mutual recursion. For , the definition agrees with Fix.