Алгоритм Хиндли-Милнера в Java - PullRequest
11 голосов
/ 22 июля 2011

Я работаю над простой системой, основанной на потоке данных (представьте, что это редактор / среда выполнения LabView), написанной на Java. Пользователь может соединить блоки вместе в редакторе, и мне нужен вывод типа, чтобы убедиться, что граф потока данных корректен, однако, большинство примеров вывода типов написаны в математических обозначениях, ML, Scala, Perl и т. Д., Которые я не говорю ».

Я прочитал об алгоритме Хиндли-Милнера и нашел этот документ с хорошим примером, который я мог бы реализовать. Он работает на множестве ограничений типа T1 = T2. Тем не менее, мои графики потоков данных переводятся в T1> = T2 как ограничения (или T2 расширяет T1, или ковариацию, или T1 <: T2, как я видел это в различных статьях). Никаких лямбд, просто введите переменные (используемые в обобщенных функциях, таких как <code>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-языка очень приветствуются.

Ответы [ 2 ]

10 голосов
/ 27 июля 2011

Я прочитал не менее 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 - это функция структурной декомпозиции, котораяв этом простом случае просто вернет одноэлементный список, содержащий новое отношение TypeRelation его параметров.

Структуры отслеживания:

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 пусто, для LowerBounds of T задано значение String.Верхних границ для T нет, поэтому TypeRelations остаются без изменений.

2.) Rel is (Integer, T);Случай 3 снова активирован.Reflexives по-прежнему пустые, нижняя граница T установлена ​​на пересечении String и Integer, что дает Object, все еще нет верхних границ для T и никаких изменений в TypeRelations

3.) Rel is T> = U.Случай 1 активирован.Поскольку Reflexives пусто, верхние границы T объединяются с верхними границами U, который остается пустым.Затем нижние границы U устанавливаются в нижние границы T, что дает Object> = U. TypeRelation (T, U) добавляется к Reflexives.

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 is T> = U. Случай 2 активирован.В этом случае делается попытка объединить верхнюю границу T (в данном случае это Object) с Integer, что приводит к сбою и сбою алгоритма.

Расширения к системе типов

Добавление общих типов в систему типов требует расширения в основных случаях и в функции SubC.

Type = { TypeVariable, ConcreteType, ParametricType<Type,...>)

Некоторые идеи:

  • Если встречаются ConcreteType и ParametricType,это ошибка.
  • Если встречаются TypeVariable и ParametricType, например, T = C (U1, ..., Un), то создайте новые переменные типа и отношения как T1> = U1, ...,Tn> = Un и работать с ними.
  • Если два ParametricType встречаются (D <> и C <>), проверьте, совпадают ли D> = C и количество аргументов типа, затем извлеките каждую пару как отношения.
3 голосов
/ 22 июля 2011

Алгоритм Хиндли-Милнера по своей сути является алгоритмом объединения, т. Е. Алгоритмом решения изоморфизмов графов для уравнений с переменными графов.

Хиндли-Милнер не имеет прямого отношения к вашей задаче, но поиск Google пришелчерез некоторые ведет;например, «Прагматический подтип в полиморфных языках» , в котором говорится «Мы представляем расширение подтипа для системы типов Хиндли / Милнера, основанное на неэквивалентности имени ...» .(Я не читал это.)


... однако, большинство примеров вывода типов написаны в математических обозначениях, ML, Scala, Perl и т. Д., Которые я не делаю«говорить».

Я думаю, вам придется преодолеть это препятствие самостоятельно.Теория типов и проверка типов являются в основном математическими ... и сложными.Вы должны поставить трудные дворы, чтобы подобрать язык.

...