-
Notifications
You must be signed in to change notification settings - Fork 571
Make instance names optional by generating them based on types #4085
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
Make instance names optional by generating them based on types #4085
Conversation
I'd be in favor of opaque generated names ( |
I agree that we should make it hard to end up relying on what the compiler chooses for an instance name, but having the scheme be just a |
As for FFI-friendliness, I don't think that's a concern; we are gradually pushing users away from calling functions with constraints from JavaScript. For example, #3182 was a big step in this direction. |
Sounds good to me. Could you help guide me on how/where this number should be generated? |
How about Edit: or something like truncating at 25-ish characters would work for me too. |
I think I prefer truncating rather than only including the class name too. It's been too long since I've been in this code and so I don't really remember, but I'd recommend following the pattern used in other places where we generate names (e.g. for unknown types when type checking). I think using GenIdent together with MonadSupply for generating the integers should work. |
I think that if you generate the names in a desugaring step rather than in the CST->AST conversion, it might be simpler and it will match better with the existing patterns in the compiler. Of course it's not really ideal that doing it in a desugaring step means that the AST type will have to use a Maybe for the name as well, but we intend to get rid of explicit instance names eventually anyway (at least I think we do?) and so since we'd be able to get rid of that Maybe then, having to add the occasional |
I'll ask a dumb question. Why not ignore explicit names immediately and generate names using the truncation idea above? Is it because this would be a breaking change in case someone (who knows where) is using an instance in FFI? Or is there something else going on here I don't know of? |
Yeah, that's partly why I was originally asking "where" to generate the number. |
Not a dumb question! I was thinking I’d like to continue using the declared instance names where they exist for as long as we are going to accept them, and that is indeed because of FFI - specifically because there may be cases where people are calling PureScript functions with constraints from the target language (eg JS). The compiler has no way of knowing when this is happening, so I think it will be worth being slightly more cautious. Also, I think it’s a little harder to justify going straight to ignoring the names with no advance notice because we haven’t (yet) explicitly come out and said anywhere that calling functions with constraints from outside PureScript is unsupported and going to become impossible as far as I know. |
While working on this last summer I ended up with the following scheme for generating unique but still readable instance names, following the usual convention:
Arguments in application chains are separated by a single dollar and are printed according to the scheme for instances arguments. We don’t need to consider rows, variables nor determined arguments outside of instance chains because of overlaps, but we can avoid collisions easily enough by appending the instance position in the chain. There’s a slight hurdle though: this scheme needs access to both the names written by the programmer (well I guess that’s not strictly required but fully qualified names do not help with readability), which are only available before renaming, and functional dependencies, which are only available after desugaring type classes 🙃 |
Does that mean there is preliminary work that needs to be done before this approach can be taken? Or that a different approach should be taken? |
Sounds like this is better explained in #3426 (comment). In other words, it sounds like there is some preliminary work that needs to be done before multiple things can be unblocked. |
#4086 only blocks the smart name munging approach to this, not the quick-and-dirty truncated-string-plus- |
Thanks for clarifying. I'll keep working on this. One other question. Looking at the lines for converting instance chains from CST to AST, it seems that all instance names within an instance chain is stored as a list in each instance foo :: ClassA Foo
else instance bar :: ClassA Bar
else instance baz :: ClassA Baz will be stored as three separate
By removing the instance names, the source code would be something like this...
and the resulting
Using what would be
But how would I update the other two |
That chain field always struck me as a little bit broken, actually. I don't think it has to be a list of names; it just has to be some kind of unique identifier. I don't know why it couldn't be a |
How is |
I know it's used in Language/PureScript/TypeChecker.hs as part of |
Hm... Maybe I should spend more time reading through this repo's source code and just getting a deeper understanding of things before I continue contributing. Perhaps documenting things along the way might help, too. |
I've run out of time and will have to come back to this PR in a few weeks. This PR was to see how far I could get in a week or so because I got bored of fixing v0.14.1 PS warnings on contrib libraries. When I return to this, I'd like to read through more of the codebase and continue from there. |
Took a quick break from other work to see where The chain is converted to a chain with qualified names, then in checkOverlappingInstances the list is used only once to prevent an Essentially, if we can accomplish that equality check with something functionally equivalent, then it unblocks the next part of this PR. |
Latest update on this PR. After getting a better understanding of the general flow of this code, I took the following approach:
A few questions I have:
|
Regardless, this PR is ready for feedback. |
GenIdent causes an error in properNameToJSON
Latest commit changes this...
to this
I didn't change the left-most or right-most dollars as it's not yet clear from other core members whether that should be done. Changing the middle $ chars to _ seemed reasonable to make without additional feedback. |
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 left some comments on the instances names generation, nothing too important though. Also, do we even need to have separators since we don’t rely on them to disambiguate similar instances (for example both C (Foo Bar) Baz
and C Foo (Bar Baz)
yield $C_Foo_Bar_Baz
, to which we then append an unique suffix)? Is $dollarModule_ClassName_Array_S$dollar42
really more legible than $dollarModuleClassNameArrayStr$dollar42
?
argName t1 <> "$" <> (N.runOpName $ qualName op) <> "$" <> argName t2 | ||
TypeArr _ t1 _ t2 -> argName t1 <> "_Arrow_" <> argName t2 | ||
TypeConstrained{} -> "" | ||
TypeUnaryRow{} -> "EmptyRow" |
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.
Row Type
is rendered as Row_Type
, so I think # Type
should render to the same string:
TypeUnaryRow{} -> "EmptyRow" | |
TypeUnaryRow{} -> "Row" |
@rhendric for what it's worth, I definitely consider you to be a real maintainer. If you don't feel that way, I think that's more of an indication that we haven't quite sorted out our governance model just yet than anything else. |
A few thoughts on this:
|
Any other changes on this? Or am I good to squash and merge? |
I was thinking about this PR just a few minutes ago and I realized that we can likely merge the In my original approach, the Once this PR changed the While the code might be more readable by keeping these two passes separate, should the instance name generation be merged into the |
Description of the change
Fixes #4084.
The biggest question here is how the name generator should work.
GenIdent
) would be a simpler and better approachThe next question is how this code should be tested. I could take all current instance tests and add a variation of each file's code to see if things still work/fail when the
name ::
part is removed from the instance. Since the code would otherwise be the same AST, I'm not sure that's necessary. A better approach would be testing whether names are unique and short enough.Checklist: