我如何在 OCaml 中定义符号?

How can I define notation in OCaml?

我正在尝试为我在 OCaml 项目中定义的类型定义符号。基本上这种类型允许我表达一个类型的一个或两个值,我称之为 maybe_pair。我希望能够编写一些符号来定义这种类型的值,例如:

我基本上是在尝试模仿列表符号的工作方式,但我相信这可能是不可能的。

下面的类型可以包含一个或两个 'a:

类型的值
type 'a maybe_pair = Single of 'a | Double of 'a * 'a

您无法仅通过定义 OCaml 函数来获得所需的符号。您可以将 <> 定义为中缀运算符(具有固定的 pre-defined 优先级),但不能将它们定义为像括号一样成对工作。

您可以使用 OCaml 语法扩展机制 ppx 获得任何所需的语法。但这是一个很大的主题,对于 Whosebug 上的答案来说太大了(在我看来)。

您可以在此处阅读有关 PPX 的信息:https://ocamlverse.github.io/content/ppx.html

这里:https://github.com/ocaml-ppx/ppxlib

您可以写一个 ppx-rewriter 来部分实现您的目标,尽管不支持带尖括号的符号。

我假设这对定义如下:

(* file: pair.ml *)

type 'a maybe_pair = MkPair of 'a * 'a option [@@deriving show]

ppx 扩展名应该如下所示:

(* file: ppx_pair.ml *)

open Ppxlib

let name = "mp"

let expand ~loc ~path:_ expr =
  match expr with
  | {pexp_desc = Pexp_sequence (e1,e2); _} -> [%expr Pair.MkPair ([%e e1], Some [%e e2]) ]
  | e1 -> [%expr Pair.MkPair ([%e e1], None) ]



let ext =
  Extension.declare name Extension.Context.expression
    Ast_pattern.(single_expr_payload __)
    expand


let () = Driver.register_transformation name ~extensions:[ext]

我们可以看到它的实际效果如下:

let () =
  let x = [%mp 1] in
  Format.printf "x is %a\n" (Pair.pp_maybe_pair Format.pp_print_int) x;
  let x = [%mp 1; 2] in    
  Format.printf "x is %a\n" (Pair.pp_maybe_pair Format.pp_print_int) x

输出:

x is (Pair.MkPair (1, None))
x is (Pair.MkPair (1, (Some 2)))

我在这里制作了一个最低限度的工作示例项目结构: https://gitlab.com/gopiandcode/example-ppxlib