ppxlib
ppxlib copied to clipboard
`Ast_patter.map0` actually call the continuation when it has only one argument
For instance,
open Ppxlib
let ext =
Extension.declare "example" Extension.Context.module_expr
Ast_pattern.(
ptyp
(map0 ~f:"foo" @@ ptyp_constr (lident (string "foo")) nil
||| map0 ~f:"bar" @@ ptyp_constr (lident (string "bar")) nil))
(fun ~loc ~path:_ arg ->
Stdlib.Printf.eprintf "expand: %s\n" arg;
Ast_builder.Default.pmod_structure ~loc [])
let () = Ppxlib.Driver.register_transformation "example" ~extensions:[ ext ]
applied on
module _ = [%example: bar]
will write
expand: foo
expand: bar
instead of only
expand: bar
The reason is that map0 will actually call the continuation when this one has one argument, even though the "matching" is not finished:
let map0 (T func) ~f = T (fun ctx loc x k -> func ctx loc x (k f))
I think (but I'm not confident) that one way to fix this would be to abstract the continuation to make sure it is never executed too early:
type ('matched_value, 'k, 'k_result) t =
| T of (context -> Location.t -> 'matched_value -> (unit -> 'k) -> 'k_result)
As a workaround before this is fixed, map_result can be used to protect the continuation to be executed:
open Ppxlib
let ext =
Extension.declare "example" Extension.Context.module_expr
Ast_pattern.(
ptyp
(map0 ~f:"foo"
@@ map_result ~f:(fun k -> k ())
@@ ptyp_constr (lident (string "foo")) nil
||| map0 ~f:"bar"
@@ map_result ~f:(fun k -> k ())
@@ ptyp_constr (lident (string "bar")) nil))
(fun ~loc ~path:_ arg () ->
Stdlib.Printf.eprintf "expand: %s\n" arg;
Ast_builder.Default.pmod_structure ~loc [])
let () = Ppxlib.Driver.register_transformation "example" ~extensions:[ ext ]
Thanks @ceastlund for finding the bug and the minimal example!