Types 类型化引用单元格?

Types 类型化引用单元格?,types,ocaml,union,dereference,ref,Types,Ocaml,Union,Dereference,Ref,我是一个不速之客。使用普通的旧ref-to-int或其他简单的内置类型,到目前为止,在所有方面对我来说都是正常的。我在元组上下文中使用了它们,其中ref是元组的成员。我可以更新引用,取消引用,等等 # let e = 1, ref 1;; val e : int * int ref = (1, {contents = 1}) # snd e := 2;; - : unit = () # e;; - : int * int ref = (1, {contents = 2}) # !(

我是一个不速之客。使用普通的旧ref-to-int或其他简单的内置类型,到目前为止,在所有方面对我来说都是正常的。我在元组上下文中使用了它们,其中ref是元组的成员。我可以更新引用,取消引用,等等

 # let e = 1, ref 1;;
 val e : int * int ref = (1, {contents = 1})
 # snd e := 2;;
 - : unit = ()
 # e;;
 - : int * int ref = (1, {contents = 2})
 # !(snd e);;
 - : int = 2
但是,一旦我将一个命名类型“of ref”声明给其他一些聚合(甚至是内置的简单)类型,通常情况下就会很糟糕。我发现我不能再像以前那样修改引用了,因为它们没有被声明为某个对象的“ref”类型。这个and:=运算符失败

语义似乎以奇怪的、明显不一致的方式发生了变化。下面只是一个例子。为什么在下面写第一个代码块是合法的,但在上面的循环中做类似的事情似乎是非法的?编译器接受第一个块,其中我们可以匹配构造类型的ref,并使用!第13行和第14行的操作员。这都在循环队列的上下文中,并使用#use:

在顶部循环中,类似的工作(以及在此基础上的变化)失败,如下所示,我还使用一个函数分解一个元组,然后尝试调用同一个操作符:

let rec elem = (1, Pointer (ref elem));;
let last = !(next elem);;
Characters 12-22:
let last = !(next elem);;
          ^^^^^^^^^^
Error: This expression has type int pointer
   but an expression was expected of type 'a ref
是的,我正在使用-rectypes,但我想在不使用递归缩写类型的情况下尝试一下,从那以后我就一直坚持使用它。请注意,以下内容在顶部循环中起作用,但我不确定它是否等效,以及我真正想要的是什么:

let last = next elem;;
val last : int pointer = (* ...  *)
如果第14行的第一个代码块更改为不使用!接线员,它坏了。重写(如下所示)会导致enqueue函数通过编译器,但行为不正常:

 (*compiles but fails - que only ever holds one item*)
let enqueue queue x = 
  match !queue with
      None ->
    let rec elem = (x, Pointer (ref elem)) in 
    queue := Some elem;
    | Some (_, Pointer last_newest_next) ->
      let oldest = last_newest_next in
      let elem = (x, Pointer oldest) in
      last_newest_next := elem;
      queue := Some elem;;
那一定是因为没有了!运算符(以及其他一些更改),倒数第二行实际上是使elem中的指针指向自身,而不是更新不同的指针(从匹配的分解元素中)以按照最初的意图指向elem。不管怎样,我仍然不明白为什么类型化ref的top-loop元组分解和从ml文件执行相同的分解之间的语义看起来不一致……如果这是所有这些的原因的话。或者,模式匹配的分解与通过函数分解元组不同吗

我使用了一个出列函数来测试上述函数的行为:

let dequeue queue = 
  match !queue with
      None -> raise Not_found
    | Some (_, Pointer oldest_ref) ->
      let oldest = !oldest_ref in
      let (x, Pointer next_ref) = oldest in
      let next = !next_ref in
      if next == oldest then
    queue := None
      else 
    oldest_ref := next;
      x;;

我能理解为什么我可能想避开函数式语言中的ref单元格,但我需要知道在必要时如何使用它们(没有双关语)。

我很难在你写的东西中找到一个特定的问题。然而,OCaml并不是不一致或不合逻辑的。这是“现代”FP语言的魅力之一——它们的类型系统基于可靠的数学。现在,我将重点关注你展示的第一件不起作用的事情:

# let rec elem = (1, Pointer (ref elem));;
# let last = !(next elem);; ## TYPE ERROR HERE
如果你只看一下
下一个元素是什么,问题似乎很清楚。从
elem
的定义中可以看出,
下一个elem
指针(ref elem)
。这不是推荐信。它的构造函数是
指针
。因此,应用
是没有意义的运算符,这就是类型错误告诉您的内容。如果要从
next elem
中获取
elem
,则需要对
指针
构造函数进行解构

# let unpointer (Pointer x) = x;;
# let last = !(unpointer (next elem));;
# last == elem;;
- : bool = true
# 

编辑:无论如何,你的循环列表类型在我看来有点复杂。如果您确实需要循环列表,可以查看OCaml中出现的双链接列表实现:。这是一个简单的底层实现,看起来很像用C编写的东西。最好使用内置的OCaml列表!在多年的OCaml编码中,我从未觉得有必要使用循环列表(只有一个数据点)。

我很难在您编写的内容中找到一个特定的问题。然而,OCaml并不是不一致或不合逻辑的。这是“现代”FP语言的魅力之一——它们的类型系统基于可靠的数学。现在,我将重点关注你展示的第一件不起作用的事情:

# let rec elem = (1, Pointer (ref elem));;
# let last = !(next elem);; ## TYPE ERROR HERE
如果你只看一下
下一个元素是什么,问题似乎很清楚。从
elem
的定义中可以看出,
下一个elem
指针(ref elem)
。这不是推荐信。它的构造函数是
指针
。因此,应用
是没有意义的运算符,这就是类型错误告诉您的内容。如果要从
next elem
中获取
elem
,则需要对
指针
构造函数进行解构

# let unpointer (Pointer x) = x;;
# let last = !(unpointer (next elem));;
# last == elem;;
- : bool = true
# 

编辑:无论如何,你的循环列表类型在我看来有点复杂。如果您确实需要循环列表,可以查看OCaml中出现的双链接列表实现:。这是一个简单的底层实现,看起来很像用C编写的东西。最好使用内置的OCaml列表!在多年的OCaml编码中,我从未觉得有必要使用循环列表(只有一个数据点)。

您似乎对编程语言的语义理解极低。在这种情况下,坚持低调似乎让你误入歧途。OCaml中引用的语义是一致的,您在这里观察到的所有奇怪现象都是由于您的错误,而不是语言语义

如果这对您有所帮助,那么下面是一种向低实现级别的人描述OCaml语义的方法。这不是我通常对初学者的描述,但如果你坚持用指针、聚合和指针来思考:

  • OCaml中的值要么可以用整数表示,要么是不可变的,按原样传递,要么是指向某个堆块的指针;在这种情况下,易变性更容易推理,因为在一种通过值/引用进行区分的语言中,一切都是sh