在OCaml中为哈希表定义类型别名

时间:2015-03-11 18:36:15

标签: json ocaml

我正在尝试使用atdgen,它要求您定义您尝试将其转换为JSON的OCaml对象的类型,称为“atd文件”

因此,对于Hashtbl.t,atdgen会生成如下代码:

type ('a, 'b) bucketlist =  
    | Empty       
    | Cons of 'a * 'b * ('a, 'b) bucketlist

type ('a, 'b) tbl = ('a, 'b) Hashtbl.t = {                                       
    mutable size: int;
    mutable data: ('a, 'b) bucketlist array;
    mutable seed: int; 
    initial_size: int; 
}

并且编译器抛出一个:

错误:此变体或记录定义与以下内容不匹配   类型('a,'b)Hashtbl.t。他们的种类不同。

我不知道如何以某种方式定义别名atdgen将生成代码,帮助我将Hashtbl序列化为JSON。因为我在stdlib/Hashtbl.ml中进行了验证,并且类型defs看似喜欢。

我来across this question,看起来它可以帮助我,但我无法弄清楚建议与atdgen生成的内容有什么不同。

以下是我的atd def的样子:

type ('a, 'b) bucketlist = [
    | Empty  
    | Cons of ('a * 'b * ('a, 'b) bucketlist)               
] <ocaml repr="classic">
type ('a, 'b) tbl <ocaml predef module="Hashtbl" t="t"> = {                      
    size <ocaml mutable>: int;  
    data <ocaml mutable>: ('a, 'b) bucketlist list <ocaml repr="array">;  
    seed <ocaml mutable>: int; 
    initial_size: int; 
}  

2 个答案:

答案 0 :(得分:4)

如果哈希表的键是字符串或者是整数,我建议坚持使用JAM对象和OCaml端的use a wrapper

如果你需要支持任意类型的键,你可能应该使用2个元素的数组数组,因为JSON没有提供更好的东西。

这是一个完整的例子,说明了两种情况:

档案table.atd

(*
  If the keys of the hash table are strings (or maybe ints),
  use the standard JSON representation as an object:
*)
type 'v table_as_object =
  (string * 'v) list <json repr="object">
    wrap <ocaml t="(string, 'v) Table.t"
                module="Table">

(*
  If you need to support keys of arbitrary types,
  you probably should use an array of arrays of 2 elements because JSON
  doesn't offer anything better:
*)
type ('k, 'v) table_as_array =
  ('k * 'v) list
    wrap <ocaml t="('k, 'v) Table.t"
                module="Table">

type stuff = {
  x: int;
}

type table_ar = (string, stuff) table_as_array
type table_obj = stuff table_as_object

档案table.ml

type ('k, 'v) t = ('k, 'v) Hashtbl.t

let of_list l =
  let tbl = Hashtbl.create (2 * List.length l) in
  List.iter (fun (k, v) -> Hashtbl.add tbl k v) l;
  tbl

let to_list tbl =
  Hashtbl.fold (fun k v l -> (k, v) :: l) tbl []

let wrap = of_list
let unwrap = to_list

档案test_table.ml

open Table_t

let main () =
  let tbl = Hashtbl.create 10 in
  Hashtbl.add tbl "abc" { x = 123 };
  Hashtbl.add tbl "def" { x = 456 };
  let json_ar = Table_j.string_of_table_ar tbl in
  let json_obj = Table_j.string_of_table_obj tbl in
  print_endline (Yojson.Basic.prettify json_ar);
  print_endline (Yojson.Basic.prettify json_obj)

let () = main ()

构建命令:

atdgen -t table.atd
atdgen -j -j-std table.atd
ocamlfind ocamlopt -o test_table \
  table.ml table_t.mli table_t.ml table_j.mli table_j.ml test_table.ml \
  -package atdgen -linkpkg

输出:

$ ./test_table 
[ [ "abc", { "x": 123 } ], [ "def", { "x": 456 } ] ]
{ "abc": { "x": 123 }, "def": { "x": 456 } }

答案 1 :(得分:3)

我对atdgen一无所知,但在我看来,Hashtbl.t是一个抽象类型。您不能将其定义为与具体记录类型相同的类型。这可能是编译器在说出种类不同时的含义。

# module A : sig type 'a t end =
      struct type 'a t = { l : 'a list } end;;
module A : sig type 'a t end
# type 'a myt = 'a A.t = { l : 'a list };;
Error: This variant or record definition does not match that of type 'a A.t
       Their kinds differ.

(换句话说,你不能在不违反Hashtbl模块的抽象层的情况下将哈希表的内部结构打包成JSON。)