| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
I'd be in favor of opaque generated names ($1). If you omit the instance name, you shouldn't rely on what the compiler chooses for an instance name. So the compiler shouldn't export a name that you might think you could rely on. All that and it would be simpler. |
Sorry, something went wrong.
|
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 $ followed by an incrementing integer would hurt the readability of the generated code quite a bit, and reading the generated code is useful from time to time. What if, as a compromise, we smush the type names together and then append a number? That way hopefully readability is still preserved, but the number at the end is still going to make it difficult to rely on. |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
Sounds good to me. Could you help guide me on how/where this number should be generated? |
Sorry, something went wrong.
|
How about className$1, as opposed to classNameArgArgArg$1? I'm sympathetic about readability but some of those smushed names could get so long as to hinder readability in a different way (think monadStateStateHalogenMStateActionSlotsOutputM$1). Edit: or something like truncating at 25-ish characters would work for me too. |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
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 fromMaybe (internalError ...) when accessing an instance name that we know exists doesn't seem too bad to me in the meantime. |
Sorry, something went wrong.
|
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? |
Sorry, something went wrong.
Yeah, that's partly why I was originally asking "where" to generate the number. convertDeclaration runs in the List monad, so I don't have access to freshIdent as a side effect. Also, is it not possible to generate the number when the parser is running? Or does that incur other problems? |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
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 🙃 |
Sorry, something went wrong.
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? |
Sorry, something went wrong.
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. |
Sorry, something went wrong.
|
#4086 only blocks the smart name munging approach to this, not the quick-and-dirty truncated-string-plus-freshIdent approach. There are a couple of reasons to prefer the latter: it's likely simpler than what the implementation of #4086 would be, and I think it's more honest—this change does seem like a revision of the AST, not just the concrete surface syntax, and translating unnamed instances to uniquely-named instances does seem like a desugaring step. |
Sorry, something went wrong.
|
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 AST.TypeInstanceDeclaration value. For example: instance foo :: ClassA Foo
else instance bar :: ClassA Bar
else instance baz :: ClassA Bazwill be stored as three separate AST.TypeInstanceDeclaration where the chainId value stores the entire list as ["foo", "bar", "baz"]: AST.TypeInstanceDeclaration ann ["foo", "bar", "baz"] 0 "foo" deps className tys body AST.TypeInstanceDeclaration ann ["foo", "bar", "baz"] 1 "bar" deps className tys body AST.TypeInstanceDeclaration ann ["foo", "bar", "baz"] 2 "baz" deps className tys body By removing the instance names, the source code would be something like this... instance ClassA Foo else instance ClassA Bar else instance ClassA Baz and the resulting AST.TypeInstanceDeclaration would be this: -- foo AST.TypeInstanceDeclaration ann [Nothing, Nothing, Nothing] 0 Nothing deps className tys body -- bar AST.TypeInstanceDeclaration ann [Nothing, Nothing, Nothing] 1 Nothing deps className tys body -- baz AST.TypeInstanceDeclaration ann [Nothing, Nothing, Nothing] 2 Nothing deps className tys body Using what would be baz as an example, I believe I can generate an instance name for baz's instance since I still have the className and tys values available. Thus, the AST would be updated to be something like this: -- baz AST.TypeInstanceDeclaration ann [Nothing, Nothing, Just "$$ClassName$Baz$2"] 2 "$$ClassName$Baz$2" deps className tys body But how would I update the other two Nothings (i.e. foo and bar's names)? While the className value should remain the same in an instance chain, I do not know what foo and bar's tys arguments are. |
Sorry, something went wrong.
|
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 (ModuleName, SourcePos), or anything else that's convenient to make unique. |
Sorry, something went wrong.
|
How is chainId even used? As a key in some Map? |
Sorry, something went wrong.
|
I know it's used in Language/PureScript/TypeChecker.hs as part of checkOverlappingInstance. I think but am not 100% confident that this is its one and only purpose. |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
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. |
Sorry, something went wrong.
|
Took a quick break from other work to see where chainId is used. Here's where it's used: The chain is converted to a chain with qualified names, then in checkOverlappingInstances the list is used only once to prevent an OverlappingInstance error from being thrown. Also, the tcdChain function comes from the TypeClassDictionary type Essentially, if we can accomplish that equality check with something functionally equivalent, then it unblocks the next part of this PR. |
Sorry, something went wrong.
|
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:
|
Sorry, something went wrong.
|
Regardless, this PR is ready for feedback. |
Sorry, something went wrong.
GenIdent causes an error in properNameToJSON
|
Latest commit changes this... $dollarModule_ClassName$dollarArray$dollarString$dollarBo$dollar42 to this $dollarModule_ClassName_Array_String_Bo$dollar42 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. |
Sorry, something went wrong.
There was a problem hiding this comment.
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?
Sorry, something went wrong.
| 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.
Row Type is rendered as Row_Type, so I think # Type should render to the same string:
| TypeUnaryRow{} -> "EmptyRow" | |
| TypeUnaryRow{} -> "Row" |
Sorry, something went wrong.
|
@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. |
Sorry, something went wrong.
A few thoughts on this:
|
Sorry, something went wrong.
|
Any other changes on this? Or am I good to squash and merge? |
Sorry, something went wrong.
|
I was thinking about this PR just a few minutes ago and I realized that we can likely merge the desugarTypeClassInstanceNames pass into the desugarTypeClasses pass. These two passes originally needed to be separate passes because of how my original approach worked. In my original approach, the chainId was the name of the instances. Since some instances' names might clash, we needed to figure out all instance names in a given module first, and then determine what their final instance name was. If any clashed, we'd get the DuplicatePartialInstanceName error. If we didn't need to determine all initial instance names in a module, then the code for generating a unique identifier could be done in the desugarTypeClasses pass, right? Once this PR changed the chainId to use the "filename and source positions" unique identifier approach proposed by @rhendric, the above requirement got dropped. I don't think anyone realized that at the time. While the code might be more readable by keeping these two passes separate, should the instance name generation be merged into the desugarTypeClasses pass? Would that help compiler performance? |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Description of the change
Fixes #4084.
The biggest question here is how the name generator should work.
The 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: