Алгоритм Хиндли-Милнера в Java
Я работаю над простой системой на основе потока данных (представьте, что это как редактор LabView/среда выполнения), написанная на Java. Пользователь может соединять блоки вместе в редакторе, и мне нужен вывод типа, чтобы убедиться, что график потока данных правильный, однако большинство примеров вывода типа написаны в математических обозначениях, ML, Scala, Perl и т. д. на котором я не "говорю".
Я прочитал об алгоритме Хиндли-Милнера и нашел этой документ с хорошим примером, который я мог бы реализовать. Он работает на множестве ограничений T1 = T2. Однако мои графики потока данных преобразуются в T1 >= T2 как ограничения (или T2 расширяет T1, или ковариацию, или T1 T merge(T in1, T in2)) и конкретные типы.
чтобы повторить алгоритм HM:
Type = {TypeVariable, ConcreteType}
TypeRelation = {LeftType, RightType}
Substitution = {OldType, NewType}
TypeRelations = set of TypeRelation
Substitutions = set of Substitution
1) Initialize TypeRelations to the constraints, Initialize Substitutions to empty
2) Take a TypeRelation
3) If LeftType and RightType are both TypeVariables or are concrete
types with LeftType <: RightType Then do nothing
4) If only LeftType is a TypeVariable Then
replace all occurrences of RightType in TypeRelations and Substitutions
put LeftType, RightType into Substitutions
5) If only RightType is a TypeVariable then
replace all occurrences of LeftType in TypeRelations and Substitutions
put RightType, LeftType into Substitutions
6) Else fail
Как я могу изменить исходный алгоритм HM для работы с такого рода отношениями вместо простых отношений равенства? Пример Java-ish или я был бы весьма признателен за объяснение.
2 ответов
Я прочитал не менее 20 статей и нашел одну (Франсуа Потье: вывод типа при наличии подтипа: от теории к практике), которую я мог бы использовать:
вход:
Type = { TypeVariable, ConcreteType }
TypeRelation = { Left: Type, Right: Type }
TypeRelations = Deque<TypeRelation>
вспомогательные функции:
ExtendsOrEquals = #(ConcreteType, ConcreteType) => Boolean
Union = #(ConcreteType, ConcreteType) => ConcreteType | fail
Intersection = #(ConcreteType, ConcreteType) => ConcreteType
SubC = #(Type, Type) => List<TypeRelation>
ExtendsOrEquals может рассказать о двух конкретных типах, если первый расширяет или равен второму, например, (String, Object) = = true, (Object, String) = = false.
Union вычисляет общий подтип двух конкретных типов, если это возможно, например, (Object, Serializable) = = Object&Serializable, (Integer, String) = = null.
пересечение вычисляет ближайший супертип двух конкретных типов, например, (List, Set) = = Collection, (Integer, String) = = Object.
SubC-это функция структурного разложения, которая в этом простом случае просто вернет одноэлементный список, содержащий новую Типереляцию его параметров.
отслеживание структуры:
UpperBounds = Map<TypeVariable, Set<Type>>
LowerBounds = Map<TypeVariable, Set<Type>>
Reflexives = List<TypeRelation>
UpperBounds отслеживает типы, которые могут быть супертипы типа переменной, LowerBounds отслеживает типы, которые могут быть подтипы типа переменной. Reflexives отслеживает отношения между переменными типа пар, чтобы помочь в переписывании алгоритма.
алгоритм выглядит следующим образом:
While TypeRelations is not empty, take a relation rel
[Case 1] If rel is (left: TypeVariable, right: TypeVariable) and
Reflexives does not have an entry with (left, right) {
found1 = false;
found2 = false
for each ab in Reflexives
// apply a >= b, b >= c then a >= c rule
if (ab.right == rel.left)
found1 = true
add (ab.left, rel.right) to Reflexives
union and set upper bounds of ab.left
with upper bounds of rel.right
if (ab.left == rel.right)
found2 = true
add (rel.left, ab.right) to Reflexives
intersect and set lower bounds of ab.right
with lower bounds of rel.left
if !found1
union and set upper bounds of rel.left
with upper bounds of rel.right
if !found2
intersect and set lower bounds of rel.right
with lower bounds of rel.left
add TypeRelation(rel.left, rel.right) to Reflexives
for each lb in LowerBounds of rel.left
for each ub in UpperBounds of rel.right
add all SubC(lb, ub) to TypeRelations
}
[Case 2] If rel is (left: TypeVariable, right: ConcreteType) and
UpperBound of rel.left does not contain rel.right {
found = false
for each ab in Reflexives
if (ab.right == rel.left)
found = true
union and set upper bounds of ab.left with rel.right
if !found
union the upper bounds of rel.left with rel.right
for each lb in LowerBounds of rel.left
add all SubC(lb, rel.right) to TypeRelations
}
[Case 3] If rel is (left: ConcreteType, right: TypeVariable) and
LowerBound of rel.right does not contain rel.left {
found = false;
for each ab in Reflexives
if (ab.left == rel.right)
found = true;
intersect and set lower bounds of ab.right with rel.left
if !found
intersect and set lower bounds of rel.right with rel.left
for each ub in UpperBounds of rel.right
add each SubC(rel.left, ub) to TypeRelations
}
[Case 4] if rel is (left: ConcreteType, Right: ConcreteType) and
!ExtendsOrEquals(rel.left, rel.right)
fail
}
простой пример:
Merge = (T, T) => T
Sink = U => Void
Sink(Merge("String", 1))
отношения этого выражения:
String >= T
Integer >= T
T >= U
1.) rel is (String, T); Случай 3 активирован. Потому что Reflexives пуст, нижние границы T установлены в строку. Нет верхних границ для T, поэтому Типереляции остаются неизменными.
2.) rel (целое число, T); Случай 3 активируется снова. Рефлексы по-прежнему пусты, нижняя граница T установлена на пересечение строки И целого числа, что дает объект, по-прежнему нет верхних границ для T и никаких изменений в Типереляциях
3.) rel - T >= U. корпус 1 активирован. Поскольку рефлексы пусты, верхние границы T в сочетании с верхней границей U, которая остается пустой. Затем нижние границы U устанавливаются на нижние границы ot T, давая Object >= U. Типереляция (T, U) добавляется к Рефлексивам.
4.) алгоритм завершается. Из границ Object >= T и Object >= U
в другом примере демонстрируется конфликт типов:
Merge = (T, T) => T
Sink = Integer => Void
Sink(Merge("String", 1))
отношения:
String >= T
Integer >= T
T >= Integer
шаги 1.) и 2.) такие же, как и выше.
3.) rel - T >= U. корпус 2 активирован. Случай пытается объединить верхнюю границу T (которая является объектом в этой точке) с целым числом, которое терпит неудачу, и алгоритм терпит неудачу.
расширения для системы типа
добавление универсальных типов в систему типов требует расширения в основных случаях и в функции SubC.
Type = { TypeVariable, ConcreteType, ParametricType<Type,...>)
некоторые идеи:
- если ConcreteType и ParametricType встречаются, то это ошибка.
- если TypeVariable и ParametricType встречаются, например, T = C (U1,..., Un) затем создайте переменные и отношения нового типа как T1 >= U1,... , Tn >= Un и работать с ними.
- если встречаются два параметрических типа (D и C), проверьте, совпадают ли D >= C и количество аргументов типа, затем извлеките каждую пару как отношения.
алгоритм Хиндли-Милнера является принципиально алгоритмом унификации, т. е. алгоритмом решения изоморфизмов графа для графовых уравнений с переменными.
Hindley-Milner не относится непосредственно к вашей проблеме, но поиск Google наткнулся на некоторые зацепки; например "прагматический подтип в полиморфных языках", который сказал " мы представляем расширение подтипа для системы типа Хиндли / Милнера, которая основана на неравенстве имен ...". (Я не читал.)
... однако большинство примеров вывода типа записываются в математических обозначениях, ML, Scala, Perl и т. д. на котором я не "говорю".
Я думаю, тебе придется преодолеть это препятствие самостоятельно. Теория типов и проверка типов фундаментально математичны ... и сложно. Вам нужно поставить в жесткие ярды, чтобы подобрать язык.