Usage of `Construct` to implement missing fields of modules/functors
Hello!
I hope this message is posted at the right place, don't hesitate to tell me if it's not the case :)
What I would like
I am currently trying to implement a feature to implement all missing fields of modules/functors.
Why is it different from existing features
This is already almost possible using holes, for example:
module type T = sig val x: int end
module X:T = _
will propose to replace the hole by struct let x = _ end.
However, if I already have implemented some fields using normal declarations or includes it is not possible anymore. It wouldn't be a problem for module types like the one above, but for large functors where a huge part is derived from the children it is impossible to manually remove the already defined fields.
I think this kind of features could be interesting, and having many missing field warnings while creating functors is really annoying (everything is red and unreadable, btw it may be possible to attach the errors to the assignment or to the struct and not to the whole module definition to improve readability)!
What I wanted to do
At first, I wanted to propose a new code action in ocaml-lsp, like the one that asks to add missing rec in function definitions. But it seemed tough to construct the missing fields while iterating over the errors.
Then, I wondered if I could propose a new place where a hole can be replaced: module X(Y: T1): T2 = struct include Y _ end.
I started to edit the pattern matching https://github.com/ocaml/merlin/blob/941eb60fb664370ba58b977f826d00b94d614d25/src/frontend/query_commands.ml#L632 to match expression in structure_item in structure in module_expr in module_binding to do similar things that what is done while replacing the hole in module X:T = _ (the first case of the pattern matching).
To this purpose, I added an optional argument to the construct.ml main function used to replace the hole to indicate which structure_item to ignore based on the typing environment of the matched expression (ignore already defined fields).
What my current problem is
When I write module X:T = _ the hole is a Module_expr that is a Tmod_constraint which contains the required type T.
While in module X:T = struct _ end the type of the module_expr (the hole), the structure_item, the structure, and the type of the identifier in the module_binding (evaluated in the environment in the scope of the module binding) all are equal to an empty module.
TL;DR; I can't find the constraint X:T anymore, neither in the typed AST, nor in the typing environment. I guess this is because the typing of X failed.
Conclusion
Then, do you think this feature could be interesting? Do you have an idea if it is feasible? If not do you think there is another way?
Thank you for your reading and have a good weekend! Jérôme Boillot
PS: I'm tagging @voodoos because I think you worked on the computation of holes that are modules :)
Hello @jboillot, yes, this is the right place to ask :-)
If I understand well you would like a command that would complete the skeleton of a module given its signature.
I don't think reusing construct with another kind of holes (which are neither expressions or module expressions) is the best way to do it. Your first idea of adding a new command for that purpose seems more natural to me.
I don't think you need, or want to parse errors to implement it, but instead you could use the typed-tree to check the current type of the module implementation against its expected signature. Not that the compiler does inclusion checks in includemod.ml and you may be able to reuse that. The type signature_symptom looks really interesting for your purpose... (I am just waving my hands here :-) )
(once you get the list of missing siganutre_items you could re-use code in construct to generate the corresponding structure items)
I don't have a lot of time for this right now but don't hesitate to ping me if you decide to dig deeper !