MDX: how to configure ppx extensions?

Hello, is it possible to use a ppx with mdx?

Given these 2 files:


Dune file

(mdx
 (libraries async async_kernel ppx_let))

Markdown file

MDX ok

```ocaml
let add a b = a + b
```

---

MDX ok

```ocaml
# add 1 2
- : int = 3
```

---

MDX not ok: `Error: Uninterpreted extension 'map'`

```ocaml
open Async;;
open Deferred.Result.Let_syntax;;
let promise =
  let%map number = Deferred.return (Ok 1) in number;;
```

When I run dune runtest, I get a compilation error.

Unfortunately, Dune’s preprocess stanza is not compatible inside the mdx stanza so I’m not sure if what I want is possible at the moment.

1 Like

I’m not exactly sure how to do it for PPX extensions based on the standard infrastructure, but I was able to do it for those based on camlp5 and it was very simple: just act as if MDX is a standard top level – I don’t mean a dune top level but rather a normal top level – and dump in the commands that you would need to enable PPX rewriting In that top level.

So I suppose this might not help in the sense that now you have to figure out how to do PPX rewriting in a normal top level. But I seem to remember somebody wrote that up somewhere.

I doubt this will be useful to you because this is based on camlp5, but here’s an MDX compatible doc file that I wrote a while back: https://github.com/camlp5/pa_ppx_regexp/blob/master/README.asciidoc

P.S. Specifically, I call your attention to this section: https://github.com/camlp5/pa_ppx_regexp/blob/master/README.asciidoc#from-the-ocaml-toplevel

Thanks. Your nudging made me think about it a little more.

I did find that I had to require β€œppx_let” if I wanted to test the async lib without a dune project, like so:

─( 14:42:01 )─< command 0 >────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop # #require "async";;
─( 14:42:01 )─< command 1 >────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop # open Async;;
─( 14:42:08 )─< command 2 >────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop # open Deferred.Result.Let_syntax;;
─( 14:42:13 )─< command 3 >────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop # let promise = let%map number = Deferred.return (Ok 1) in number;;
Error: Uninterpreted extension 'map'.
─( 14:42:18 )─< command 4 >────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop # #require "ppx_let";;
─( 14:42:23 )─< command 5 >────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop # let promise = let%map number = Deferred.return (Ok 1) in number;;
val promise : (int, 'a) Async_kernel__Deferred_result.t = <abstr>

This also works with the standard ocaml REPL.


This made me realize I had to β€œrequire” it too, as if mimicking a REPL, then run β€œnormal” OCaml via MDX. Even though I listed ppx_let as a dependency in my MDX project. My guess is that some code doesn’t auto-load under an MDX context.

Here’s a complete example that works :


==> dune <==
(mdx
 (libraries async async_kernel async_unix core ppx_let))

==> dune-project <==
(lang dune 3.10)

(using mdx 0.4)

==> REAME.md <==
MDX ok

```ocaml
let add a b = a + b
```

---

MDX ok

```ocaml
# add 1 2
- : int = 3
```

---

MDX ok

```ocaml
# #require "ppx_let";;
# open Async;;
# open Core;;
# open Deferred.Result.Let_syntax;;
# let promise_one =
  let%map one = Deferred.return (Core.Ok 1) in one;;
val promise_one : (int, 'a) Async_kernel__Deferred_result.t = <abstr>
```

---

MDX ok!

```ocaml
open Async;;
open Core;;
open Deferred.Result.Let_syntax;;
let promise_two =
  let%map two = Deferred.return (Core.Ok 2) in two;;
```

---

MDX final check ok!

```ocaml
# let ok_one = Async.Thread_safe.block_on_async_exn @@ fun () -> promise_one;;
val ok_one : (int, 'a) Core._result = Core.Result.Ok 1
# let ok_two = Async.Thread_safe.block_on_async_exn @@ fun () -> promise_two;;
val ok_two : (int, 'a) Core._result = Core.Result.Ok 2
# add (Core.ok_exn ok_one) (Core.ok_exn ok_two);;
- : int = 3
```
1 Like

I figured out a way to use ppx for mdx, indirectly.

  1. You can make a normal sync_to_md.ml file with normal dune but with MDX wrapping comments.
(* $MDX part-begin=partName *)
type t = int [@@deriving_inline hash] [@@@end]
(* $MDX part-end *)

Just dune build your file as a normal ocaml project.

  1. Include that part in your md file with the reference tags.
<!-- $MDX file=path/to/sync_to_md.ml,part=partName -->
```ocaml
``'

Run normal mdx command dune runtest then dune promote.