Types 要统一的类型变量出现在类型中

Types 要统一的类型变量出现在类型中,types,compiler-errors,sml,unification,Types,Compiler Errors,Sml,Unification,我有一个函数可以从两个列表中重建一棵树。我返回一个关于所有分支的列表,但是我得到了一个我不理解的错误。但我认为这与返回类型有关 错误是: Can't unify ''a with ''a list (Type variable to be unified occurs in type) Found near recon ( ::( preoH, preoT), ::( inoH, ...)) Exception- Fail "Static errors (pass2)" raised 出现错误

我有一个函数可以从两个列表中重建一棵树。我返回一个关于所有分支的列表,但是我得到了一个我不理解的错误。但我认为这与返回类型有关

错误是:

Can't unify ''a with ''a list (Type variable to be unified occurs in type) Found near recon
( ::( preoH, preoT), ::( inoH, ...))
Exception- Fail "Static errors (pass2)" raised
出现错误的那一行是函数定义的标题
fun recon(preoH::preoT,inoH::inoT)=

这个错误到底意味着什么?为什么会发生

(* Reconstruts a binary tree from an inorder and a preorder list. *)
fun recon (preoH::preoT, inoH::inoT) =
  (* Case 0: Leaf reached*)
  if
      preoT = [] andalso inoT = [] andalso preoH = inoH
  then
      [preoH]
  else
      let
      (* split the list of nodes into nodes to the left and nodes to the
      right of preoH; ST stands for subtree *)
      val (inoLST, inoRST) = splitat (inoH::inoT, preoH)
      val (preoLST, preoRST) = splitafter (preoT, last(inoLST))
      in
      (* Case 1: Unary branch encountered, make preoH the parent node of the
      subtree and combine the left and right preorder and inorder lists*)
      if
              length(inoLST) <> length(preoLST)
      then
          [preoH, recon (preoLST@preoRST, inoLST@inoRST)]
      (* Case 2: Binary branch encountered, proceed as normal *)
      else
              [recon (preoLST, inoLST), preoH, recon (preoRST, inoRST)]
      end;
(*从顺序和预顺序列表中重建二叉树。*)
趣味侦察(preoH::preoT,inoH::inoT)=
(*案例0:已到达叶*)
如果
preoT=[]和also inoT=[]和also preoH=inoH
然后
[preoH]
其他的
让
(*将节点列表拆分为左侧节点和右侧节点
优先权;ST代表子树*)
val(inost,inoRST)=拆分(inoH::inoT,preoH)
val(前一次,前一次)=拆分后(前一次,最后一次(前一次))
在里面
(*情况1:遇到一元分支,将preoH设为
子树,并组合左、右预排序和顺序列表*)
如果
长度(初始)长度(初始)
然后
[预演,侦察](preoLST@preoRST, inoLST@inoRST)]
(*情况2:遇到二进制分支,继续正常操作*)
其他的
[侦察员(先发,先发),先发,侦察员(先发,先发)]
结束;

用某物统一变量意味着为变量找到一个等于该某物的值。例如,我们可以统一一些简单的东西,比如(我将使用三重相等表示两项必须相等):

统一的结果是我们可以用
a
代替的值。在这种情况下,我们可以用
int
代替
a
,方程将保持不变(类似于数学中求解方程组):

或者我们可以统一一个稍微复杂一点的方程:

a -> int === bool -> b
在这里,我们需要找到需要替换
a
b
的值,以便方程成立。这些是
a的
bool
b的
int

a: bool
b: int
---------------------------
bool -> int === bool -> int
我希望你现在已经有了这个想法。在您的情况下,编译器必须统一这一点:

a === a list
好吧,在你的错误消息中,它是
“a
,而不仅仅是
a
,但我们暂时可以忽略它。问题是,由于
a
出现在两侧,因此等式是不可统一的,因此错误消息(emphasis mine)中的提示“Type变量要统一出现在Type”中。如果我们说
a
必须是
a列表
,并用两边的
a
替换,我们会得到以下结果:

a list === a list list
我们还没有删除需要求解的
变量,我们也不会很快删除。这就是为什么编译器会出错,它会导致一个无限循环,一个简单的检查变量不会出现在等式的两边是避免该循环的好方法

你为什么会这样?简短的版本是,您试图使用嵌套列表表示树,而SML的类型系统无法处理该问题。您试图根据列表构建的树类似于:

[[a], a, [a]]
其中
a
是某种泛型类型变量。列表是同质容器,它们只能包含单一类型的值,这意味着
[a]
a
必须是相同的类型,即:

a === a list
我已经解释了为什么这会导致错误

解决方案是使用递归的
数据类型来表示树,例如:

datatype 'a tree =
  Leaf
| Node of { value : 'a, left: 'a tree, right: 'a tree }
这是因为它允许我们递归地定义它,也就是说,叶子的类型是树本身。您的
recon
函数的返回类型应该是
''树

希望现在能更清楚一点

  • Ionut全面解释了类型统一的工作原理,因此这里有一个提示:

    [preoH, recon (preoLST@preoRST, inoLST@inoRST)]
    
    第一个元素具有类型“a”,第二个元素具有类型“a”列表

    第二个元素具有类型“a”,第一个和第三个元素具有类型“a”列表

  • >P>当您检查列表为空时,考虑使用<代码> null PROOT ,或使用模式匹配处理该情况。


    @约翰克莱曼谢谢你,先生!非常感谢,尤其是来自一位教授。@IonuțG.Stan:
    recon
    可能应该在树中键入“列表->”或“列表->”,因为元素正在相互比较。@SimonShine你是对的。我只是想避免解释
    'a
    'a
    之间的区别,但这可能会导致更多的混淆。如果您碰巧对ML中的类型推断工作方式感兴趣,我已经对此进行了一个简短的讨论:我希望口音可以忍受。
    datatype 'a tree =
      Leaf
    | Node of { value : 'a, left: 'a tree, right: 'a tree }
    
    [preoH, recon (preoLST@preoRST, inoLST@inoRST)]
    
    [recon (preoLST, inoLST), preoH, recon (preoRST, inoRST)]