Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Place all derived functions into a functor? #529

Open
benbellick opened this issue Sep 25, 2024 · 3 comments
Open

Place all derived functions into a functor? #529

benbellick opened this issue Sep 25, 2024 · 3 comments

Comments

@benbellick
Copy link

Hello!

I'm working on a Ppxlib deriving extension which transforms types into functions (specifically deriving serialization decoders).

I would like to somehow make all of the generated functions encapsulated into a functor that I can then instantiate with a module.

E.g.

(*In file `file.ml` *)
type a = int [@deriving my_ext]
(* makes `a_fun`*)
type b = int * string [@deriving my_ext]
(* makes `b_fun`*)

(* I somehow want to have the above `a_fun` and `b_fun` *) placed into a functor like `File_my_ext` *)

let My_gen_mod = File_my_ext(<MODULE>)

One possible solution is to make it so that

type a = int [@deriving my_ext]
type b = int * string [@deriving my_ext]

generates code like

module File_my_ext (Input : Sig) = struct end
type a = int [@deriving my_ext]

module File_my_ext (Input : Sig) = struct
  include File_my_ext(Input)
  let a_fun = <FUN>
end

type b = int * string [@deriving my_ext]
module File_my_ext (Input : Sig) = struct
  include File_my_ext(Input)
  let b_fun = <FUN>
end

I believe this would work, though it feels quite clunky as I am having to create all of these intermediary functors and instantiate them along the way. Is there a more natural way to do this?

If its relevant, that top level of my extension consists of

Deriving.add name ~str_type_decl |> Deriving.ignore

for some implementation of str_type_decl

Thanks!!

@NathanReb
Copy link
Collaborator

Unfortunately what you suggest is not strictly possible. There are a few things you could do to make it work:

  1. You can deal with groups of type declarations, if I reuse your above example, you can group both your type declarations together so that the deriver is applied to all of them at once:
type a = int
and b = int * string
[@@deriving my_ext]
  1. You can also group your type declarations in a module and write a rule to generate your functor based on the types declared in said module. The deriving API currently only supports module types but we could consider extending it if needed. E.g. atm you could do:
module type S = sig
  type a = int

  type b = int * string
end
[@@deriving my_ext]

Given you are trying to generate a functor, doing so from a module type seems like a more logical approach!

If you give me a bit more context on what deriver you are working on and how you plan to use it I may be able to provide more helpful hints!

@benbellick
Copy link
Author

Ah thanks a ton for the help!

Upon further thought, I am thinking it may make the most sense to just have the user of the extension encapsulate the generated functions in a module by hand, i.e.:

module S  (D : MOD_TYPE) = struct
  open D
  type a = int [@@deriving my_ext]

  type b = int * string [@@deriving my_ext]
end

let module Instantiated = S(Some_D)

I don't love having to make the user encapsulate their types into a functor, but I think it will work easiest for my current implementation.

@benbellick
Copy link
Author

I actually changed my mind and decided I like approach 2 you suggested above better! But I'm still a bit unsure how to get it quite right.

To give you a little bit more context, I am working on a deriving library for decoders. It's a decoding library that allows you to use decoder combinators to write decoders for a multiple formats (e.g. JSON, XML, etc.).

Here is what I would like to happen:

module X = struct 
  type t = int * string 
end [@@deriving decoders]

--via-ppx-->

module X = struct 
  type t = int * string 
  module Encode (D : Decoders.Decode.S) = struct 
    let t_decode = <SOME IMPLE USING D>
  end 
end

It does already work to do:

module X = struct 
  module D = <MAKE D AVAILABLE>
  type t = int * string [@@deriving decoders]
end 

--via-ppx-->

module X = struct 
  module D = <MAKE D AVAILABLE>
  type t = int * string 
  let t_decoder = <IMPLE USING D>
end

I tried the approach with using signatures, but the problem is that the implementation needs to be dumped into the implementation module, rather than the signature module.

Any guidance would be much appreciated. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants