e
is an arbitrary expression, and x
is an arbitrary variable, then λx.e
is an expression. Here it helps to think about e
as an ordinary (but not necessarily) complex expression that includes x
, i.e. x
² +2
, and then about λx.e
as an anonymous function, which takes input x
and returns the result of evaluating the expression e
for the given value x
. Simply put, think of it as function(x) { return x^2 + 2; }
f
and e
are valid expressions, then f(e)
also valid. It is called an “overlay” ( Application ) for obvious reasons.x
is a variable, and e
1 and e
0 are valid expressions, then replacing every occurrence of x
in e
0 with e
1 will also give a valid expression. That is, if, for example, e
1 = x
² +2
and e
0 = y/3
, then putting x
= e
0 in e
1 , we get the expression (y/3)
² +2
.e
0 as x
in e
1 is equivalent to using the abstraction λx.
e
1 to e
0 . It is added only to support so-called let polymorphism .]x
2 +2
(or at least 2), based on the above? Damn, what about zero? There is no line in these rules, which obviously leads to the expression 0 ". The solution in this case is to create an expression in lambda calculus that behaves like 0.1, ..., +, ×, -, / with the correct interpretation. In other words, we should encode numbers, arithmetic operations, strings, etc. into patterns that we can create using lambda syntax. In this post there is a small, but very good section on numbers and arithmetic operations. This is an interesting feature of lambda calculus: we have an elementary syntax that we can recursively define with four simple points, but it allows us to inductively prove many things in four basic steps, since the language itself has expressive power to write numbers, strings and all types operations that we may need.e
be any expression such that " e
" is a variable in our meta-language, denoting any expression from our base language. For example, such as any of the following: x Math.pow(x,2) [1,2].forEach ( function(x) { print(x); } )
t
is an arbitrary type, then we can express " e
has type t
" throughe:t
e
, t
is a variable in our meta-language that can correspond to any type in the base language (int, string, etc.). And just like for e
, for t
compliance with any particular type is not required.s
and t
are types, then t
→ s;
Is the type of the function with t
at the input and s
at the outputr
is a type, possibly composed of other types (just as t
→ s
is composed of t
and s,
each of which, in turn, can potentially be represented as a composition of some other types), and α
is a variable of this type, then ∀ α.r
is a type. function (x) { return x; }
String
→ String
. Or Int
→ Int
. In fact, for any type t
its type is t
→ t
. We will say that it is of type ∀ tt
→ t
. Each of the types String
→ String
or t
→ t
is a “monotype”. ∀ tt
→ t
is a “polytype”. The function identical to that written above has an abstract polytype ∀ tt
→ t
, which in practice means that for any real type t
it has type t
→ t
. Putting all of the above together, we get just such a compact summary record:xx:
∀ α.α
→ α
Suppose I am already able to infer that the variableduck
is of typeAnimal
.
Moreover, suppose that I deduced thatspeak
is a method of typeAnimal
->String
.
Then I can deduce thatspeak(duck)
is of typeString
.
And any reasoning that fits into such a form is a valid type inference.
x:t
means that if we take a statement from Γ as an assumption / axiom / existing knowledge, then we can infer that x
is of type t
.x:t
∈ Γ says that the statement x:t
belongs to Γ.y
has type σ from Γ, then we can infer that x
has type τ from Γ.Source: https://habr.com/ru/post/185686/
All Articles