Java中的Hindley-Milner算法

aka*_*okd 11 java algorithm type-inference hindley-milner

我正在研究一个用Java编写的基于数据流的简单系统(想象它就像一个LabView编辑器/运行时).用户可以在编辑器中将块连接在一起,我需要类型推断以确保数据流图是正确的,但是,大多数类型推断示例都是用数学符号,ML,Scala,Perl等编写的,我不会"说" ".

我看了一下辛德米尔纳算法,发现这个文件有一个很好的例子,我可以实现.它适用于一组T1 = T2之类的约束.但是,我的数据流图转换为T1> = T2,就像约束一样(或T2延伸T1,或协方差,或T1 <:T2,正如我在各篇文章中看到的那样).没有lambdas只是类型变量(在通用函数中使用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
Run Code Online (Sandbox Code Playgroud)

如何更改原始HM算法以使用这些关系而不是简单的平等关系?Java-ish示例或解释将非常感激.

aka*_*okd 10

我阅读了至少20篇文章并找到了一篇文章(Francois Pottier:存在子类型的类型推断:从理论到实践),我可以使用它:

输入:

Type = { TypeVariable, ConcreteType }
TypeRelation = { Left: Type, Right: Type }
TypeRelations = Deque<TypeRelation>
Run Code Online (Sandbox Code Playgroud)

助手功能:

ExtendsOrEquals = #(ConcreteType, ConcreteType) => Boolean
Union = #(ConcreteType, ConcreteType) => ConcreteType | fail
Intersection = #(ConcreteType, ConcreteType) => ConcreteType
SubC = #(Type, Type) => List<TypeRelation>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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
  }
Run Code Online (Sandbox Code Playgroud)

一个基本的例子:

Merge = (T, T) => T
Sink = U => Void

Sink(Merge("String", 1))
Run Code Online (Sandbox Code Playgroud)

这个表达的关系:

String >= T
Integer >= T
T >= U
Run Code Online (Sandbox Code Playgroud)

1.)rel是(String,T); 案例3已激活.因为Reflexives为空,所以T的LowerBounds设置为String.没有T的UpperBounds,因此,TypeRelations保持不变.

2.)rel是(整数,T); 案例3再次激活.Reflexives仍为空,T的下限设置为String和Integer的交集,产生Object,仍然没有T的上限,TypeRelations没有变化

3.)rel是T> = U.案例1被激活.因为Reflexives是空的,T的上界与U的上界相结合,U的上界保持为空.然后将下界U设置为T的下界,产生Object> = U.TypeRelation(T,U)是Reflexives的addet.

4.)算法终止.从边界Object> = T和Object> = U.

在另一个示例中,演示了类型冲突:

Merge = (T, T) => T
Sink = Integer => Void

Sink(Merge("String", 1))
Run Code Online (Sandbox Code Playgroud)

关系:

String >= T
Integer >= T
T >= Integer
Run Code Online (Sandbox Code Playgroud)

步骤1.)和2.)与上述相同.

3.)rel是T> = U.案例2被激活.该案例尝试将T的上界(此时为Object)与Integer结合,失败并且算法失败.

Type系统的扩展

将泛型类型添加到类型系统需要在主要情况和SubC函数中进行扩展.

Type = { TypeVariable, ConcreteType, ParametricType<Type,...>)
Run Code Online (Sandbox Code Playgroud)

一些想法:

  • 如果ConcreteType和ParametricType相遇,那就是错误.
  • 如果TypeVariable和ParametricType满足,例如,T = C(U1,...,Un),则创建新的Type变量和关系,如T1> = U1,...,Tn> = Un并使用它们.
  • 如果两个ParametricType满足(D <>和C <>),则检查D> = C且类型参数的数量是否相同,然后将每个对提取为关系.