@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

Итог

  • Синтаксис → правила типизации → алгоритм проверки типов
  • Вывод типов
  • "Простое типизированное лямбда-исчисление"
  • Лямбда-куб
  • Сложности реальных языков

Литература

  1. Pierce, B. (2002). Types and programming languages. Cambridge, Mass.: MIT Press.
  2. Кольцов, М. (2018). Добровольная типизация в Python 3. PiterPy Meetup. https://www.youtube.com/watch?v=EU9DoJD1olo
  3. Брагилевский, В. (2017). Программирование с зависимыми типами на языке Idris, весна 2017. https://compsciclub.ru/courses/idrisprogramming/2017-spring/
 

Вопросы

Неформальное введение в теорию типов

By Maxim Koltsov

Неформальное введение в теорию типов

  • 1,553