@maksbotan
21 апреля 2020 г.
Максим Кольцов
Неформальное введение в теорию типов
Обо мне
- Мат-Мех СПбГУ
- Haskell
- Serokell
- Biocad
План
- Игрушечный Python
- Проверка типов
- Сложности в настоящем Python
- Более сложные системы
Игрушечный Python
0, 1, 2, 42, ...
True, False
x, y, z, ...
x * 5, x == 0
x if y else z
lambda x: 2 * x
(lambda x: 2 * x)(21)
lambda f: lambda x: f(f(x))
Числа
Логика
Переменные
Арифметика
Условия
Функции
Вызов
Выражения
42, True
2 * 4
5 * True
int
2 * 4
int
int
int
2 * 4
int
int
\dfrac{2: \mathrm{int} \quad 4: \mathrm{int}}{2 * 4: \mathrm{int}}
?
2 * a
int
?
\Gamma = \{ a: \mathrm{int} \}
\Gamma \vdash v : t
"Контекст"
\( v \) имеет тип \( t \) в контексте \( \Gamma \)
"отношение типизации"
\dfrac{
a: \mathrm{int} \in \Gamma
\quad
b: \mathrm{int} \in \Gamma
}{
\Gamma \vdash a * b: \mathrm{int}
}
2 * a
\dfrac{
\Gamma \vdash a: \mathrm{int}
\quad
\Gamma \vdash b: \mathrm{int}
}{
\Gamma \vdash a * b: \mathrm{int}
}
\dfrac{
x: t \in \Gamma
}{
\Gamma \vdash x: t
}
(a + b) * c
\dfrac{
}{
\vdash 0: \mathrm{int}
}
\dfrac{
}{
\vdash \mathrm{True}: \mathrm{bool}
}
v1 if c else v2
bool
t
t
t
\Big \{
\dfrac{
\Gamma \vdash c: \mathrm{bool} \quad
\Gamma \vdash v_1: t \quad
\Gamma \vdash v_2: t
}{
\Gamma \vdash v_1\ \mathrm{if}\ c\ \mathrm{else}\ v_2: t
}
lambda x: 2 * x
\dfrac{
\Gamma, x: t_1 \vdash v: t_2
}{
\Gamma \vdash \mathrm{lambda}\ x: v: t_1 \to t_2
}
v
\Big \{
Если \( x : \mathrm{int} \), то \( v : \mathrm{int} \)
Расширенный контекст
(lambda x: 2 * x)(21)
\dfrac{
\Gamma \vdash f : t_1 \to t_2 \quad
\Gamma \vdash v : t_1
}{
\Gamma \vdash f(v): t_2
}
Правила типизации
\dfrac{
\Gamma \vdash a: \mathrm{int}
\quad
\Gamma \vdash b: \mathrm{int}
}{
\Gamma \vdash a * b: \mathrm{int}
}
\dfrac{
v: t \in \Gamma
}{
\Gamma \vdash v: t
}
\dfrac{
\Gamma \vdash c: \mathrm{bool} \quad
\Gamma \vdash v_1: t \quad
\Gamma \vdash v_2: t
}{
\Gamma \vdash v_1\ \mathrm{if}\ c\ \mathrm{else}\ v_2: t
}
\dfrac{
\Gamma, x: t_1 \vdash v: t_2
}{
\Gamma \vdash \mathrm{lambda}\ x: v: t_1 \to t_2
}
\dfrac{
\Gamma \vdash f : t_1 \to t_2 \quad
\Gamma \vdash v : t_1
}{
\Gamma \vdash f(v): t_2
}
Игрушечный Python
- Синтаксис
- Тип
- Контекст
- Отношение типизации
- Правило типизации
Итог
Проверка типов
\dfrac{
\Gamma \vdash a: \mathrm{int}
\quad
\Gamma \vdash b: \mathrm{int}
}{
\Gamma \vdash a * b: \mathrm{int}
}
def typecheck(env, expr) -> typ:
...
if expr == a * b:
if typecheck(a) == int and typecheck(b) == int:
return int
else:
raise TypeCheckError("a * b")
Модуль ast
>>> ast.dump(ast.parse('2*4'))
Module(body=[
Expr(value=
BinOp(
left=Num(n=2),
op=Mult(),
right=Num(n=4)
)
)])
expr
class PrimType(Enum):
ty_int = "int"
ty_bool = "bool"
Type = Union[PrimType, "FunctionType"]
class FunctionType(NamedTuple):
arg: Type
res: Type
Env = Dict[str, Type]
class TypeCheckError(Exception):
pass
def typecheck(expr, env: Env) -> Type:
if isinstance(expr, ast.Num):
return PrimType.ty_int
if (isinstance(expr, ast.NameConstant)
and expr.value in (False, True)):
return PrimType.ty_bool
if isinstance(expr, ast.Name):
ty = env.get(expr.id)
if ty is not None:
return ty
\dfrac{
v: t \in \Gamma
}{
\Gamma \vdash v: t
}
\dfrac{
}{
\vdash 0: \mathrm{int}
}
\dfrac{
}{
\vdash \mathrm{True}: \mathrm{bool}
}
if isinstance(expr, ast.BinOp):
if (isinstance(expr.op,
(ast.Add, ast.Mult, ast.Sub, ast.Div))):
ty_left = typecheck(expr.left, env)
ty_right = tyecheck(expr.right, env)
if ty_left == ty_right == PrimType.ty_int:
return PrimType.ty_int
if isinstance(expr, ast.Compare):
ty_left = typecheck(expr.left, env)
ty_right = typecheck(expr.comparators[0], env)
if ty_left == ty_right:
return PrimType.ty_bool
\dfrac{
\Gamma \vdash a: \mathrm{int}
\quad
\Gamma \vdash b: \mathrm{int}
}{
\Gamma \vdash a * b: \mathrm{int}
}
\dfrac{
\Gamma \vdash a: t
\quad
\Gamma \vdash b: t
}{
\Gamma \vdash a == b: \mathrm{bool}
}
if isinstance(expr, ast.IfExp):
ty_compare = typecheck(expr.test, env)
ty_then = typecheck(expr.body, env)
ty_else = typecheck(expr.orelse, env)
if (ty_compare == PrimType.ty_bool
and ty_then == ty_else):
return ty_then
\dfrac{
\Gamma \vdash c: \mathrm{bool} \quad
\Gamma \vdash v_1: t \quad
\Gamma \vdash v_2: t
}{
\Gamma \vdash v_1\ \mathrm{if}\ c\ \mathrm{else}\ v_2: t
}
if isinstance(expr, ast.Lambda):
arg = expr.args.args[0]
body = expr.body
new_env = copy(env)
new_env[arg.arg] = PrimType.ty_int
body_type = typecheck(body, new_env)
return FunctionType(PrimType.ty_int, body_type)
\dfrac{
\Gamma, x: t_1 \vdash v: t_2
}{
\Gamma \vdash \mathrm{lambda}\ x: v: t_1 \to t_2
}
if isinstance(expr, ast.Call):
ty_func = typecheck(expr.func, env)
ty_arg = typecheck(expr.args[0], env)
if (isinstance(ty_func, FunctionType)
and ty_func.arg == ty_arg):
return ty_func.res
\dfrac{
\Gamma \vdash f : t_1 \to t_2 \quad
\Gamma \vdash v : t_1
}{
\Gamma \vdash f(v): t_2
}
Примеры
$ python3 typecheck.py '2 * 2' int
$ python3 typecheck.py '(2 * 2) + 4' int
$ python3 typecheck.py 'x * 5' __main__.TypeCheckError: Name(id='x', ctx=Load())
$ python3 typecheck.py 'lambda x: 2 * x' int -> int
$ python3 typecheck.py 'lambda x: x if x > 0 else 0' int -> int
$ python3 typecheck.py 'lambda x: lambda y: x if x > 0 else y' int -> int -> int
$ python3 typecheck.py '(lambda x: x if x > 0 else 0)(5)' int
Вывод типов
lambda x: 2 * x
: ?
\( \Gamma = \{ \} \)
infer(lambda x: 2 * x)
infer(2 * x)
\( \Gamma = \{ x : \tau_1 \} \)
infer(2)
infer(x)
\( \tau_1 \)
int
\dfrac{
\Gamma \vdash a: \mathrm{int}
\quad
\Gamma \vdash b: \mathrm{int}
}{
\Gamma \vdash a * b: \mathrm{int}
}
2 * x : int \( \Gamma = \{ x : \tau_1 \} \) \( C = \{ \tau_1= \mathrm{int} \} \)
\dfrac{
\Gamma, x: t_1 \vdash v: t_2
}{
\Gamma \vdash \mathrm{lambda}\ x: v: t_1 \to t_2
}
lambda x: 2 * x : int \( \to \) int
mypy
class ExpressionChecker(ExpressionVisitor[Type]):
def visit_call_expr(self, e: CallExpr) -> Type:
...
def visit_int_expr(self, e: IntExpr) -> Type:
...
def visit_op_expr(self, e: OpExpr) -> Type:
...
def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
...
def visit_list_expr(self, e: ListExpr) -> Type:
...
pyre
match value with
| BooleanOperator { BooleanOperator.left; operator; right } ->
...
| Call
{ callee =
{ Node.value = Name (Name.Identifier "super"); _ } as callee;
arguments
} ->
...
| Integer literal ->
...
| List elements ->
...
Проверка и вывод типов
Итог
- Парсинг: модуль ast
- Проверка типов для игрушечного языка
- Вывод типов: генерация и решение систем уравнений
Проблемы настоящего Python
[]
def foo(x):
if x == 5:
return True
return x * 2
n = 5
x = 10
if n > 0:
x = x + n
else:
x = x - n
\Gamma \vdash x : \tau
Другие системы
f : \mathrm{bool} \to \mathrm{int}
f : \forall a.\ a \to a
f : \mathrm{List\ int} \to \mathrm{Optional\ int}
f : n : \mathrm{int} \to
\mathrm{Vector}\ n\ \mathrm{int} \to
\mathrm{Vector}\ n\ \mathrm{int} \to
\mathrm{Vector}\ n\ \mathrm{int}
f : \forall a.\
\mathrm{List}\ a \to \mathrm{Optional}\ a
Подтипы
\dfrac{
\Gamma \vdash v : S \quad
S <: T
}{
\Gamma \vdash v : T
}
\dfrac{}{
\{ x: \mathrm{int}, y :\mathrm{int} \} <: \{ x: \mathrm{int} \}
}
Переменные
\dfrac{\Gamma \vert \emptyset \vdash v : \tau}
{\Gamma \vert x: \tau \vdash x = v : \mathrm{Unit}}
\dfrac{
\Gamma \vert \Sigma_1 \vdash v_1 : \mathrm{Unit} \quad
\Gamma \cup \Sigma_1 \vert \Sigma_2 \vdash v_2 : \tau
}{
\Gamma \vert \Sigma_1 \cup \Sigma_2 \vdash v_1 \mathrm{;}\; v_2 : \tau
}
\Gamma \vert \Sigma \vdash v : \tau
Итог
- Синтаксис → правила типизации → алгоритм проверки типов
- Вывод типов
- "Простое типизированное лямбда-исчисление"
- Лямбда-куб
- Сложности реальных языков
Литература
- Pierce, B. (2002). Types and programming languages. Cambridge, Mass.: MIT Press.
- Кольцов, М. (2018). Добровольная типизация в Python 3. PiterPy Meetup. https://www.youtube.com/watch?v=EU9DoJD1olo
- Брагилевский, В. (2017). Программирование с зависимыми типами на языке Idris, весна 2017. https://compsciclub.ru/courses/idrisprogramming/2017-spring/
Вопросы
Неформальное введение в теорию типов
By Maxim Koltsov
Неформальное введение в теорию типов
- 1,553