Consider the following code. I define a type once, then I repeat myself 3 times
- create table
- first part of insert values
- second part of insert values
This then happens for EVERY struct/table I define. This is quite error prone, as I’m looking at tens of structs/tables.
In theory, I believe ppx can help me reduce this type of repetition. However, I’m new to ppx. Can someone help with a minimal sample macro? I’m mainly looking for a minimal “skeleton” I can then hack on.
module Row__Browser__Self_Event = struct
type t = {
self_id: Fixed_String_16.t;
self_count: int;
self_time: Time_Milli.t;
tag_0: Fixed_String_16.t;
tag_1: Fixed_String_16.t;
tag_2: Fixed_String_16.t;
data: string;
};;
let drop_tbl = {sql| DROP TABLE IF EXISTS main_logs.browser_self |sql};;
let create_tbl = {sql|
CREATE TABLE IF NOT EXISTS main_logs.browser_self
(
self_id FixedString(16),
self_count UInt32,
self_time_ymd UInt32,
self_time_hmsm UInt32,
tag_0 FixedString(16),
tag_1 FixedString(16),
tag_2 FixedString(16),
data String,
) ENGINE = MergeTree()
ORDER BY ()
|sql};;
let insert_into = {sql|
INSERT INTO main_logs.browser_self ( self_id, self_count, self_time_ymd, self_time_hmsm, tag_0, tag_1, tag_2, data) VALUES
|sql};;
let insert_values (v: t) (module Q: Single_Quote_Util) = String.concat " " [
"(";
Q.quote_string v.self_id.raw;
Q.quote_int v.self_count;
Q.quote_int v.self_time.year_month_day;
Q.quote_int v.self_time.hour_minute_second_milli;
Q.quote_string v.tag_0.raw;
Q.quote_string v.tag_1.raw;
Q.quote_string v.tag_2.raw;
Q.quote_string v.data;
")"
];;
end
EDIT: Better minimal example:
module Row__Browser__Min = struct
type t = {
self_count: int;
data: string;
};;
let drop_tbl = {sql| DROP TABLE IF EXISTS main_logs.browser_self |sql};;
let create_tbl = {sql|
CREATE TABLE IF NOT EXISTS main_logs.browser_self
(
self_count UInt32,
data String,
) ENGINE = MergeTree()
ORDER BY ()
|sql};;
let insert_into = {sql|
INSERT INTO main_logs.browser_self ( self_count, data) VALUES
|sql};;
let insert_values (v: t) (module Q: Single_Quote_Util) = String.concat " " [
"(";
Q.quote_int v.self_count;
Q.quote_string v.data;
")"
];;
end
I think this strips out all non-primitive types, yet still captures the “core” of the problem that there is one definition + 3 repetitions, and the 3 repetitions can be replaced via a macro.
Is there a readable impl of @@deriving fields
from Records - Real World OCaml ? Understanding that macro should suffice for everything I need.
The ppxlib repo has two examples, one of which is a deriver, which derives accessor functions from a record type: ppxlib/examples/simple-deriver at main · ocaml-ppx/ppxlib · GitHub
If you are new to PPX, you might be interested in Preprocessors and PPXs · OCaml Tutorials (for some general introduction to PPXs) and Documentation · ppxlib 0.29.1 · OCaml Packages (for some material about writing PPXs).
2 Likes
@panglesd : Thanks for the instructive example. I typed up the code, and after fixing many typos I introduced, the code compiled.
For debugging purposes, how do I “printf” the output of the macro?
I.e. I want the output after macro expansion, but before it starts rewriting into whatever IR it uses.
I’m using dune for all my projects.
Thanks!
If you mean you want to see the code that the ppx generates (ie after expansion), you can use
$ ocamlc -stop-after parsing -dsource <file.pp.ml> > blah.generated.ml
That file.pp.ml
will be in the _build
dir.
Edit: this article from Tarides is a nice ppx intro if you’re looking for more reading materials.
1 Like
This is insane. I did not know you could specify SHELL SCRIPTS as “preprocessors” in dune. Truly fascinating.
1 Like