As I told you before, performance is something desirable when programming a λ interpreter. The sustitutions we saw are made in one step.
I will note the beta step as →β and a substitution step as →. For instance,
(λx.x) z →β x[x/z] → z.
The computations of the traditional lambda calculus are always:
T →β T' → → → → until no more substitutions can be made →β T'' → → →...
Now, if we want to control the substitutions, one can make some computations like
T →β T' few steps of sustitutions →β T'' →β T''...
To make it clear, supose the term
(λx. y) ((λx. x x) z)
One posible reduction in the traditional λ calculus is
(λx. y) ((λx. x x) z) →β (λx. y) (x x)[x/z] → (λx. y) (x[x/z] x[x/z]) → (λx. y) (z x[x/z]) → (λx. y) (z z) → y[x/(z z)] → y
But a more clever path is posible, if we don't make all the substitutions:
(λx. y) ((λx. x x) z) →β (λx. y) (x x)[x/z] → y[x/(x x)[x/z]] → y
So a calculus with explicit substitution is a calculus that has the substitutions as another rewriting rules. For instance, λx is the simpler calculus:
(λx. M) N | →β | M[x/N] |
x[x/R] | → | R |
y[x/R] | → | y |
(T T’)[x/R] | → | T[x/R] T’[x/R] |
(λy.T)[x/R] | → | (λy.T[x/R]) |
That's the idea behind explicit substitutions. It's very simple and yet has many issues I will show in future posts.