我如何在 OCaml 中定义符号?
How can I define notation in OCaml?
我正在尝试为我在 OCaml 项目中定义的类型定义符号。基本上这种类型允许我表达一个类型的一个或两个值,我称之为 maybe_pair
。我希望能够编写一些符号来定义这种类型的值,例如:
- 对于单个值,我们可以写成
<5> : int maybe_pair
- 对于两个值我们可以写成
<3;7> : int 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
您可以写一个 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
我正在尝试为我在 OCaml 项目中定义的类型定义符号。基本上这种类型允许我表达一个类型的一个或两个值,我称之为 maybe_pair
。我希望能够编写一些符号来定义这种类型的值,例如:
- 对于单个值,我们可以写成
<5> : int maybe_pair
- 对于两个值我们可以写成
<3;7> : int 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
您可以写一个 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