-
Notifications
You must be signed in to change notification settings - Fork 25
Relaxed method resolution, based on sizes #271
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
Conversation
This commit proposes a relaxed restriction of a method resolution. Instead of forbidding using the same method twice, it allows multiple use of the same method, but each consecutive usage must be on the smaller type. Since unification variables have size 1, the solution is not perfect, but allows, e.g., for pretty-printing of nested lists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements a relaxed method resolution strategy that allows the same method to be used multiple times in a parameter resolution path, provided each successive usage operates on a smaller type. The previous implementation prohibited any duplicate method usage, which was overly restrictive for cases like pretty-printing nested data structures.
Key Changes:
- Replaces size-agnostic duplicate detection with size-based tracking using
int Var.Map.tinstead ofVar.Set.t - Adds a
sizefunction to compute type sizes, enabling comparison of type complexity - Updates the
restrict_varfunction to compare sizes and allow reuse when the new usage has a strictly smaller type size
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/ok/ok0139_sizedMethodResolve.fram | Adds a test case demonstrating nested method resolution on progressively smaller types |
| src/TypeInference/ParamResolve.mli | Updates the vset parameter type from Var.Set.t to int Var.Map.t |
| src/TypeInference/ParamResolve.ml | Implements size-based restriction logic and updates all vset initializations |
| src/TypeInference/Constr.mli | Updates vset type and clarifies its usage in documentation |
| src/TypeInference/Constr.ml | Updates vset field type to match interface |
| src/Lang/UnifPriv/Type.ml | Implements size and scheme_size functions for computing type sizes |
| src/Lang/Unif.mli | Exports the new size function in the public API |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/TypeInference/Constr.mli
Outdated
| (** Set of variables that cannot be used during method resolution. | ||
| This set is just passed as [~vset] to [ParamResolve.resolve_method]. | ||
| Do not use it directly. |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation describes vset as a 'Set of variables that cannot be used' but it's now a map from variables to their sizes. The comment should clarify that the map values represent size thresholds - variables can only be reused if the new usage has a strictly smaller size than the recorded value.
| (** Set of variables that cannot be used during method resolution. | |
| This set is just passed as [~vset] to [ParamResolve.resolve_method]. | |
| Do not use it directly. | |
| (** Map from variables to their size thresholds during method resolution. | |
| A variable can only be reused if the new usage has a strictly smaller size than the recorded value. | |
| This map is just passed as [~vset] to [ParamResolve.resolve_method]. Do not use it directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, that is correct. However, the actual representation of this "variable set" is an implementation detail of the ParamResolve module, but since the method constraints should be defined earlier, the actual type representation leaks to the public interface. I added the comment "Do not use it directly" to stress that this is an implementation detail, and the actual representation doesn't matter. I don't see any point in making the documentation more precise here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like the comments to at least acknowledge in any way that vset is a map. If I were to stumble upon this fragment while working on something else, I would probably feel very lost: a variable named vset, that comments repeatedly insist is a Set, but has type int Var.Map.t.
You are right that the comment stresses to "not use it directly", and links to the very place where the type and role of vset is explained, but I still think the code as it is now is needlessly arcane.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should change vset name to something that doesn't suggest a representation, like used_vars. Or, we can introduce another module, that provides some basic functionality related to these "sets". The type would remain abstract, and the code should be cleaner, but at the cost of introducing a new module. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be perfectly happy with renaming it to used_vars. I don't think it's worth introducing a new module for such a small part of what ultimately is a stopgap measure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The longer I look at this code, the more convinced I am that we should add a separate module for this "stopgap measure". I see at least two reasons to do that:
- data abstraction — I feel really bad that some internal data representation leaks, and pollutes types in other modules;
- code maintainability — this is simple data structure with a well-defined interface. However, I can imagine the situation when this module might grow in the future. For instance we can add command-line parameters to tune looping detection, like max depth proposed on the last meeting. Or when we will add type-classes we will face additional challenges that could be addressed there. If the implementation will stay in the
ParamResolvemodule we could end up with spaghetti code, that is hard to maintain.
Foxinio
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All changes seem correct w.r.t. what was discussed.
Brychlikov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a small issue with the internal documentation, but I don't feel very strongly about it and am willing to let it go.
LGTM
src/TypeInference/Constr.mli
Outdated
| (** Set of variables that cannot be used during method resolution. | ||
| This set is just passed as [~vset] to [ParamResolve.resolve_method]. | ||
| Do not use it directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like the comments to at least acknowledge in any way that vset is a map. If I were to stumble upon this fragment while working on something else, I would probably feel very lost: a variable named vset, that comments repeatedly insist is a Set, but has type int Var.Map.t.
You are right that the comment stresses to "not use it directly", and links to the very place where the type and role of vset is explained, but I still think the code as it is now is needlessly arcane.
forell
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No complaints regarding this one from me. The idea to rename vset seems reasonable, though.
And a slight refactoring of ParamResolve
|
I decided to move cycle detection to a separate module. Moreover, as the number of parameters of functions in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
forell
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found your arguments for a separate cycle detection module convincing.
Brychlikov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! No issues with readability or documentation for me.
This PR proposes a relaxed restriction of a method resolution. Instead of forbidding using the same method twice, it allows multiple use of the same method, but each consecutive usage must be on the smaller type. Since unification variables have size 1, the solution is not perfect, but allows, e.g., for pretty-printing of nested lists.