首页 > 解决方案 > 尝试返回 Map 元组时无法理解错误

问题描述

首先,我有这些类型:

type position = float * float
type node = position

我已经编写了这些模块来制作我的地图:

module MyMap =
  struct
    type t = Graph.node
    let compare (a1,b1) (a2,b2) =
      if a1 > a2 then 1
       else if a1 < a2 then -1
       else if b1 > b2 then 1
       else if b1 < b2 then -1
       else 0
  end

module DistMap = Map.Make(MyMap)

然后我写了这个函数:

let update_distances n1 n2 dMap prevMap =
  if ((DistMap.find n2 dMap) > (DistMap.find n2 dMap) +. extract_float(dist2 n1 n2)) then
  ((DistMap.add n2 (DistMap.find n2 dMap) dMap), DistMap.add n1 prevMap)
  else (dMap,prevMap)

extract_float(dist n1 n2)n1返回从和n2节点之间的距离提取的浮点数。

为了更清楚地说明,dMap 是应该像这样构建的 (node, float) 而 prevMap 是应该像这样构建的映射: (node,node)。

我的目标是能够返回一个 Map 元组,无论是否修改,具体取决于 if 语句,但这是我得到的错误输出:

Error: This expression has type 'a but an expression was expected of type
         'a DistMap.t -> 'a DistMap.t
       The type variable 'a occurs inside 'a DistMap.t -> 'a DistMap.t

更新 :

dist2函数具有类型distance

type distance = Distance of float

这里是extract_float

let extract_float dist =
  match dist with
    | Distance x -> x

这是我第一次看到这个错误,无论如何要解决这个问题?

谢谢。

标签: dictionarytuplesocaml

解决方案


处理类型错误的第一步是尝试恢复一些上下文类型信息。

在这里,类型检查器在 prevMap 部分报告错误

else dMap,prevMap

错误:此表达式的类型为 'a,但预期的表达式类型为 'a DistMap.t -> 'a DistMap.t

类型变量 'a 出现在 'a DistMap.t -> 'a DistMap.t

此外,错误是元组的第二个元素的预期类型与实际类型不匹配。由于我们正在输入一个else分支,这意味着错误从then分支中开始,并且稍后才引发冲突。所以让我们看一下then分支(我们现在可以忽略recursive occurrence消息的一部分):

then DistMap.add n2 (DistMap.find n2 dMap) dMap, DistMap.add n1 prevMap

由于元组的第二个元素引发了错误,我们可以将错误的可能根缩小到

 DistMap.add n1 prevMap

此时,我们可以询问类型DistMap.add

node -> 'data -> 'data DistMap.t -> 'data DistMap.t

换句话说,从这里的第一次使用prevMap,我们和类型检查器可以推断出:

  • prevMap是一些要存入的数据DistMap.t
  • 元组的第二个元素是添加prevMap到 a的函数DistMap.t

在这一点上一切都很好,但随后在

else dMap, prevMap

我们知道这prevMap也是一个函数。a 中的prevMap一些数据和 type 的更新函数也是如此。这两种类型是不兼容的(如果不允许递归类型)。因此错误消息的第一部分是'a'a DistMap.t'a DistMap.t -> 'a DistMap.t

错误:此表达式的类型为 'a,但预期的表达式类型为 'a DistMap.t -> 'a DistMap.t

但是,错误消息的第二部分呢:

类型变量 'a 出现在 'a DistMap.t -> 'a DistMap.t

碰巧OCaml可以使这两种类型与-rectypes选项兼容,那么类型prevMap就是奇异的:

'a DistMap.t -> 'a DistMap.t as 'a

这意味着这prevMap是一个转换的函数,DistMap.t其中包含转换的函数,DistMap.t其中包含...的函数

这种递归类型很少使用,使用时它们可以掩盖普通的编程错误。因此默认情况下它们被禁用,并且类型检查器在禁用recursive occurrence时推断递归类型时会​​引发消息-rectype


推荐阅读