ocaml - How to write a PPX rewriter generating lenses for records? -


i writing ppx rewriter ease definition of lenses. let me recall casual reader lenses are.

about lenses

a lens associated field of record pair of functions allowing extract record , update it. here example:

module lens = struct   type ('a, 'b) t = {     : 'a -> 'b;     set : 'b -> 'a -> 'a   } end  type car = {   vendor: string;   make: string;   mileage: int; }  let vendor_lens = {   lens.get = (fun x -> x.vendor);   lens.set = (fun v x -> { x vendor = v }) } 

the vendor_lens allows value of field vendor in our car , update – means returning fresh copy of car differing original value of vendor car. might @ first sound banal not: since lenses functions, can composed , lenses module filled useful functions. ability compose accessors crucial in complex code bases, eases decoupling abstracting path computation context nested record. refactored getopts , configuration file parser adopt functional interface, makes lenses more relevant – @ least me.

generating lenses

the definition of vendor_lens above nothing more boilerplate code , there no reason 1 not take advantage of ppx-rewriters let write

type car = {   vendor: string;   make: string;   mileage: int; } [@@with_lenses] 

and see automagically definition of lenses need work our car.¹

i decided tackle problem , produce:

  1. a predicate is_record : parsetree.structure_item -> bool recognising type record definitions.

  2. a function label_declarations : parsetree.structure_item -> string list maybe returning list of record declarations record definition – yes, smash 1 , 2 using option.

  3. a function lens_expr : string -> parsetree.structure_item generating lens definition given field declaration. unfortunately discovered ppx_metaquot alain frisch after wrote function.

it seems me have here essential parts of ppx-rewriter want write. still, how can combine them together?


¹ while searching ppx-rewriter lenses, stumbled on not less 5 blogs or readmes involving same car structure. recycling example here vile attempt full-time member of selective club of lens-equipped car drivers.

the final goal of ppx project build mapper of type ast_mapper.mapper.

mapper large record type , carries mapper functions parsetree data types, example,

type mapper = {    ...   structure : mapper -> structure -> structure;   signature : mapper -> signature -> signature;   ... } 

there default mapper ast_mapper.default_mapper , starting point of mapper: can inherit , override of record members use. lens project, have implement structure , signature:

let extend super =   let structure self str = ... in   let signature self str = ... in   { super structure; signature }  let mapper = extend default_mapper 

your function structure should scan structure items , add appropriate value definition each record type declaration. signature should same thing add signatures of lens functions:

let structure self str = list.concat (list.map (fun sitem -> match sitem.pstr_desc   | pstr_type tds when tds_with_lenses sitem ->       sitem :: sitems_for_your_lens_functions   | _ -> [sitem]) str) in   let signature self str = list.concat (list.map (fun sgitem -> match sgiitem.psig_desc   | psig_type tds when tds_with_lenses sitem ->       sgitem :: sgitems_for_your_lens_functions   | _ -> [sgitem]) str) in 

super , self same of oo: super original mapper extending , self result of extension. (actually first version of ast_mapper used class instead of record type. if prefer oo style, can use ast_mapper_class of ppx_tools package, provides same in oo interface.) in anyway.. guess in case, there no need use self or super arguments.

once finish own mapper give ast_mapper.apply run mapper against input:

let () =   let infile = .. in   let outfile = .. in   ast_mapper.apply ~source:infile ~target:outfile mapper 

more or less, ppx rewriter implementations above. checking several small ppx implementations surely helps understanding.


Comments

Popular posts from this blog

php - Admin SDK -- get information about the group -

dns - How To Use Custom Nameserver On Free Cloudflare? -

Python Error - TypeError: input expected at most 1 arguments, got 3 -