130 13.4. Типизация памяти
содержимое адреса l
2
имеет тип Unit, по отношению ко второму — тип Unit Unit. Это наблюдение
немедленно ведет к первой попытке построить правило типизации для адресов:
Γ µ l : T
1
Γ l : Ref T
1
А именно, чтобы найти тип адреса l, мы смотрим на текущее содержимое ячейки с этим адресом и
вычисляем тип T
1
этого содержимого. Типом адреса тогда будет Ref T
1
.
Начав таким образом, мы должны продвинуться еще немного дальше, чтобы добиться непротиво-
речивого описания. В сущности, сделав тип терма зависимым от состояния памяти, мы превратили
трехместное отношение типизации (между контекстами, термами и типами) в четырехместное (между
контестами, содержимым памяти, термами и типами). Поскольку, с интуитивной точки зрения, со-
стояние памяти является частью контекста, в котором мы вычисляем тип терма, давайте при записи
помещать это состояние слева от символа : Γ µ t : T. Правило типизации для ссылок теперь имеет
вид
Γ µ µ l : T
1
Γ µ l : Ref T
1
и все прочие правила типизации в системе также расширяются состоянием памяти. Остальные пра-
вила не делают с этим состоянием ничего интересного — они просто передают его от предпосылки к
заключению.
Однако с этим правилом возникают две проблемы. Во-первых, проверка типов становится достаточ-
но неэффективной, поскольку вычисление типа адреса l включает вычисление типа текущего состояния
v по адресу l. Если l встречается в терме t много раз, мы будем пересчитывать тип v много раз в про-
цессе вывода дерева типизации для t. Хуже того, если сам v содержит адреса, нам придется и их тип
пересчитывать каждый раз, когда мы их встретим. Например, если состояние памяти таково:
l
1
λx:Nat. 999,
l
2
λx:Nat. (!l
1
) x,
l
3
λx:Nat. (!l
2
) x,
l
4
λx:Nat. (!l
3
) x,
l
5
λx:Nat. (!l
4
) x ,
то вычисление типа l
5
требует вычисления типов l
4
, l
3
, l
2
и l
1
.
Во-вторых, предложенное правило типизации может вообще оказаться неспособным что-либо вы-
вести, если в памяти имеется цикл. Например, нет ни одного конечного дерева вывода типизации для
адреса l
2
по отношению к памяти
l
1
λx:Nat. (!l
2
) x
l
2
λx:Nat. (!l
1
) x),
поскольку вычисление типа для l
2
требует нахождения типа для l
1
, который, в свою очередь, обраща-
ется к l
2
, и т. д. Такие циклические структуры ссылок действительно иногда встречаются на практике
(скажем, с их помощью можно построить двусвязные списки), а нам хотелось бы, чтобы наша система
типов умела работать с такими данными.
Упражнение 13.4.1 : Можете ли Вы найти терм, который при вычислении создал бы такое цик-
лическое состояние памяти?
Обе эти проблемы возникают из-за того, что наше предварительное правило типизации для адресов
требует пересчитывать тип адреса каждый раз, когда он упоминается в терме. Однако интуитивно ясно,
что это необязательно. В конце концов, когда адрес впервые создается, мы знаем, каков тип начального
значения, которое мы по этому адресу записываем. Более того, несмотря на то, что впоследствии мы
можем сохранять в той же ячейке другие значения, эти другие значения всегда будут того же типа, что
и начальное. Другими словами, для каждого адреса в памяти мы всегда имеем в виду один конкретный
тип, определяемый в момент выделения памяти. Эти типы можно собрать вместе в структуру типи-
зации памяти — конечную функцию, отображающую адреса в типы. Для обозначения таких функций
мы будем использовать метапеременную Σ.
rev. 104