ocaml - 具有递归依赖元素的反向异构 gadt 列表

标签 ocaml reverse gadt heterogeneous-list

看过this answer about the elements of a List that depend on the type of preceding elements ,问题已通过以下列表定义解决:

 type ('a,'b) mlist =
  | MNil : ('a,'a) mlist
  | MCons : ('a, 'b) element * ('b,'c) mlist -> ('a,'c) mlist

其中“元素”是:

module Tag = struct
  type root = Root
  type fruit = Fruit
  (* ... *)
end
type (_,_) element=
  | Fruit : (Tag.root, Tag.fruit) element
  | Veggie : (Tag.root, Tag.veggie) element
  (* ... and so on *)

我发现它对于创建一个列表非常有用,您想要在其中制定一些关于元素顺序的规则。例如,在神经网络中创建层列表时...

我现在想要反转这个列表。所以我定义了一个新的反转列表类型,如下所示:

 type ('a,'b) rev_list =
  | RNil : ('a,'a) rev_list
  | RCons : ('b, 'c) element * ('a,'b) rev_list -> ('a,'c) rev_list

其中最后一个元素的类型取决于前一个元素。

但是我在使用反向函数时遇到了麻烦:

let rec rev : type a b c. (a, c) mac_list ->
                    (b, a) rev_list ->
                    (c, b) rev_list =
  fun lst acc -> 
  match lst with
  | MNil -> acc (* <- error *)
  | MCons (h, t) ->
     revp t (RCons (h, acc))

显然,Ocaml 提示返回类型与累加器的类型不匹配: 类型(b,a)与类型(c,b)不兼容...

但是,如果我将 (b, a) rec_list 写为返回类型,我会在最后一行(在递归函数调用时)收到错误,因为显然,前一个列表元素的类型acc 中的内容与我现在添加的不一样。

我认为我不能在这里使用任何存在包装器,因为(据我所知)我会丢失有关列表元素的类型信息......

虽然代码是在 Ocaml 中,但如果您有 Haskell 中的解决方案或者至少有一些建议我在哪里可以搜索它,我会很高兴。

如果您知道如何限制列表或其他数据结构中元素的顺序,并且知道我还需要向后迭代它,我将不胜感激。

编辑:
看起来我把问题过于简单化了,所以把它发布在这里,我的错。
但如果原来的问题是这样的怎么办:
实际上,我的 mlistrev_list 定义如下:

type ('a,'b) mac_list =
  | MNil : ('a,'a) mlist
  | MCons : ('a, 'b) element * (('b,'c) element, _) mlist -> 
            (('a,'c) element, 'e) mlist

type ('a,'b) rev_list =
  | RNil : ('a,'a) rev_list
  | RCons : ('b, 'c) element * (('a,'b) element, _) rev_list ->
            (('a,'c) element, 'e) rev_list

我不确定这些定义是否有意义,但我确实需要有关“元素”内部类型的信息来迭代列表,因为我想传递当前元素的“水果”或“蔬菜”到下一次迭代。

我的迭代代码工作正常,直到我想反转列表:

(* ... *)
type (_,_) element=
  | Fruit : Tag.root -> (Tag.root, Tag.fruit) element
 (* ... *)

let rec iter : type a b c. ((a, b) element, c) mlist -> a 
               -> unit =
  fun list input ->
  match list with
  | MNil -> ()
  | MCons (element, tail) ->
     (match element with
      | Fruit root ->
         (* processing the input *)
         let fruit : Tag.fruit = (* to fruit *) root + input in 
         iter tail fruit
     (* ... *) )

rev 函数现在如下:

let rec rev : type a b c d. ((b, c) element, d) mlist ->
                    ((a, b) element, _) rev_list ->
                    ((a, c) element, _) rev_list =
  fun lst acc -> 
  match lst with
  | MNil -> acc 
  | MCons (h, t) ->
     rev t (RCons (h, acc))

在这种情况下,无论类型的顺序如何,我在返回累加器时都会遇到错误......

最佳答案

您距离解决方案仅差一个拼写错误, 让我们首先使用更明确的类型重写 rev 函数的类型:

let rec rev : type bottom current top.
  (current, top) mlist ->
  (bottom, current) rev_list ->
  (top, bottom) rev_list =
  fun lst acc ->
  match lst with
  | MNil -> acc (* <- error *)
  | MCons (h, t) ->
     rev t (RCons (h, acc))

如果仔细观察这两个 rev_list 的类型,会发现它们的参数顺序有些奇怪:中间列表是一个 (bottom, current) rev_list 因此保留了类型系统中的顺序 bottom -> top,同时结果是一个 (top,bottom) rev_list,它颠倒了类型参数的顺序。

这里存在不一致,这并不奇怪,因为这两个选择在 rev_list 的定义函数中是有效的。就您而言,您有:

type ('a,'b) rev_list =
  | RNil : ('a,'a) rev_list
  | RCons : ('b, 'c) element * ('a,'b) rev_list -> ('a,'c) rev_list

而另一个选择是

type ('a,'b) rpath =
  | RPNil : ('a,'a) rpath
  | RPCons : ('b, 'a) element * ('b,'c) rpath -> ('a,'c) rpath

请注意,您的选择保留了列表元素的方向,而第二个选择则相反。

因此,您一开始就选择了 ('a,'b) mlist 的反转将是 ('a,'b) rev_list 而不是 (' b,'a) rev_list`。

换句话说,要修复您的函数,我们只需要一致地记住此选择并将返回类型设置为 (bottom,top) rev_list 而不是 (顶部,底部)rev_list:

let rec rev : type bottom current top.
  (current, top) mlist ->
  (bottom, current) rev_list ->
  (bottom, top) rev_list =
  fun lst acc ->
  match lst with
  | MNil -> acc
  | MCons (h, t) ->
     rev t (RCons (h, acc))

关于ocaml - 具有递归依赖元素的反向异构 gadt 列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76814869/

相关文章:

java - 如何在 Java 中向后打印多个字符串

java - 使用递归反转字符串

floating-point - 在 OCaml 中,如何从字节数组创建 float ?

parsing - Ocaml 语法错误 : why are parentheses enclosing (if . ..) 在函数应用程序中有必要吗?

list - 从 OCaml 中的列表中返回元素列表

javascript - 使用javascript在空格后反转数组

haskell - 对受约束的 GADT 记录使用记录更新语法

haskell - 强制性和存在主义

generics - 用于Rust中多个泛型转换的GADT

compiler-errors - 编写一个函数 `smallest_absent t`,该函数返回 `l`中不存在的最小自然整数