在ocaml类型构造函数上寻找一个技巧

时间:2012-09-20 13:11:58

标签: ocaml

我有一个ocaml类型:

type t = A | B |  ...

和打印有关该类型的东西的函数:

let pp_t fmt x = match x with 
  | A -> Format.fprintf fmt "some nice explanations about A"
  | B -> Format.fprintf fmt "some nice explanations about B"
  | ...

我怎么能写一个函数来打印所有的解释?相当于:

的东西
let pp_all_t fmt = 
   Format.fprintf fmt A;
   Format.fprintf fmt B;
   ...

但如果我忘记添加新的构造函数,那会警告我。 有一些自动构建该功能的东西会更好, 因为我的问题是t很安静并且变化很大。

我无法想象如何在类型构造函数上“迭代”,但也许有一个技巧......

编辑:我最后做的是:

type t = A | B |  ... | Z
let first_t = A
let next_t = function A -> B | B -> C | ... | Z -> raise Not_found
let pp_all_t fmt = 
  let rec pp x = pp_t fmt x ; try let x = next_t x in pp x with Not_found -> ()
  in pp first_t

所以当我更新t时,编译器警告我必须更新pp_tnext_t,并且pp_all_t不必更改。 感谢大家的建议。

1 个答案:

答案 0 :(得分:2)

为了解决复杂和不断发展的类型的问题,实际上我可能会编写一个OCaml程序,该程序从包含值列表和相关信息的文件生成代码。

但是,如果你有一个函数incr_t : t -> t增加了t类型的值,并且如果你让t的第一个和最后一个值保持不变,你可以写下以下内容:

let pp_all_t fmt =
    let rec loop v =
        pp_t fmt v;
        if v < Last_t then loop (incr_t v)
     in
         loop First_t

在OCaml中不能有一般的多态incr_t,因为它只适用于构造函数为nullary的类型(不带值)。但是你可以为任何给定的类型编写自己的incr_t

这种事情在Haskell中得到了很好的处理。基本上,当定义非常明显时,编译器会为您编写一些函数。 OCaml有一个名为deriving的类似项目。我从来没有使用它,但似乎确实处理了枚举值的问题。

既然你说你想要一个“技巧”,如果你不介意使用OCaml的不安全部分(我个人介意),你可以写下incr_t如下:

let incr_t (v: t) : t =
    (* Please don't use this trick in real code :-) !  See discussion below.
     *)
    if t < Last_t then
        Obj.magic (Obj.magic v + 1)
    else
        failwith "incr_t: argument out of range"

如果可能的话,我尽量避免使用这种代码,这太危险了。例如,如果类型t获取带值的构造函数,它将产生无意义的值。真的是“等待发生的事故”。