Help getting with started ppxlib

Hi,

I would like to write a ppx rewriter which allows me to derive a ‘traversable’ given a type declaration. For example, given:

type litType = Int | Real | Str

type 'e expr =
  | Var of string
  | Lit of litType * string
  | FunApp of string * 'e list
  | TernaryIf of 'e * 'e * 'e
  | EAnd of 'e * 'e
  | EOr of 'e * 'e

I would like to be able to generate the following module definition.

module Make_traversable_expr(A:Applicative.S) : Traversable.S 
    with type 'a t := 'a expr 
     and module A := A  
= struct
    
  let traverse x ~f = 
    match x with      
    | Var n -> 
       A.return @@ Var n 

    | Lit(ty,n) -> 
       A.return @@ Lit(ty,n)

    | FunApp(fn_kind,name,args) ->      
        List.map f args 
        |> A.all 
        |> A.map ~f:(fun args -> FunApp(fn_kind,name,args))        
 
    | TernaryIf(pred,te,fe)  ->
        A.map3 ~f:(fun pred te fe -> TernaryIf(pred,te,fe)) (f pred) (f te) (f fe) 
       
    | EAnd(e1,e2) ->  
         A.map2 ~f:(fun e1 e2 -> EAnd(e1,e2) ) (f e1) (f e2)

    | EOr(e1,e2) ->  
         A.map2 ~f:(fun e1 e2 -> EOr(e1,e2) ) (f e1) (f e2)    

  end

Where Applicative is as defined in Core_kernel and Traversable.S is defined as:

module Traversable = struct
  
  module type S = sig
    module A : Applicative.S 
    type 'a t
    val traverse : 'a t -> f:('a -> 'b A.t) -> 'b t A.t
  end

end

My understanding is that ppxlib is the framework within which ppx rewriters should now be written.

I’ve been reading through the ppx_show code by @thierry-martinez but I am making very slow progress in building any sort of understanding.

Would anyone be willing to provide a brief explanation of what each of the three arguments below do?

My current understanding is that str_type_decl and sig_type_decl determines what should be generated ( a Parsetree.structure and Parsetree.signature respectively) when @@deriving show is attached to a structure or a signature but I’m not sure how they relate to extension (which generates a Parsetree.expression)

let deriver =
  Ppxlib.Deriving.add "show" ~str_type_decl ~sig_type_decl ~extension

I saw in another thread that @gasche was looking for help porting the existing ppx_deriving rewriters over to ppxlib. If someone were able to give me some guidance I would be very happy to both contribute to this effort and write this up in some detail so that others might have an easier time in doing so.

I don’t expect to be spoon-fed so any sort of guidance would be gratefully received!

Thanks,

Michael

1 Like

There are definitely people more qualified to help than me, but hopefully this is useful:

My current understanding is that str_type_decl and sig_type_decl determines what should be generated ( a Parsetree.structure and Parsetree.signature respectively) when @@deriving show is attached to a structure or a signature but I’m not sure how they relate to extension (which generates a Parsetree.expression )

You are correct about the str_type_decl and sig_type_decl parts - they need to be Deriving.Generator instances that return chunks of AST when [@@deriving] is present in struct and sig declarations respectively.

I don’t think you need to worry about extension, as that is mostly for compatibility with ppx_deriving, which you don’t seem to be aiming for.

(Shameless plug) It might be worth looking at the ppx_enum source which is a bit simpler than ppx_show as it doesn’t try to use extensions.

Some other useful resources:

2 Likes

This looks great! (I thought I’d tracked down all the ppx blogs / tutorials…). Thanks for the linked repo, too.

I don’t think you need to worry about extension , as that is mostly for compatibility with ppx_deriving , which you don’t seem to be aiming for.

Do you know what the implications of this will be if I am also using ppx_deriving for map, fold, etc.?

Cheers,

Michael

1 Like

Do you know what the implications of this will be if I am also using ppx_deriving for map , fold , etc.?

My understanding is that won’t interact as long as there isn’t a naming conflict.

Just to clarify, the extension argument is to support turning [%show: My.Module.something] into My.Module.show_something; it’s not strictly required.