-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Beginner friendly 'require' message when a constant is not found, but the constant is in the codebase but not required yet #6122
Comments
We actually ran in to this issue. I wasn't aware that the files got loaded like that. I assumed that since it's all compiled anyway, LLVM just knows how to map where everything is. I feel like this should be built in to crystal itself. But I guess until that happens, having something helpful here would be pretty cool. edit: just realized this is on the crystal repo and not lucky lol. |
Thanks for chiming in with your experience @jwoertink. I think the problem with that is that objects can be monkey patched and sometimes depend on when they're required. For example, you wouldn't want to accidentally load the monkey patched class before the original, but Crystal wouldn't know which one is which. It would be cool if it could be done, but I'm not sure it is possible/desirable because of the potential confusion around ordering. I'd love to be proven wrong though because that would be awesome. Something like autoload from Rails (but not so mystical) |
Did you ever use Ruby? It's exactly the same. |
To fix this, just explicitly require the types you need before including/extending them. |
I think it is fairly common for people to require whole folders and have it work and then suddenly stop working when they add a file that earlier in the alphabet. This can be confusing because it looks like it should be loaded. Also, a lot of people using Ruby have almost exclusively used Rails, where you often never write any requires other than |
Yeah, in Ruby, I tend to have a file called "all_the_things.rb" and then I do An easy fix here might just be adding some extra info here saying that the files are required in alphabetical order, and use an example of when that could cause an issue. |
I wanted to see if this was just me doing it, but it is likely most library authors. If you run Most every Crystal lib does this and can be bitten by it if you don't know what it is doing. A helpful message would be nice. A super simple fix would be to mention in the |
@oprypin Can you explain why you gave it a thumbs down? |
I also think it would be helpful to be aware of https://en.wikipedia.org/wiki/Curse_of_knowledge It seems so simple, but for new people, this can be a problem. At the very least something in the error message letting them know that the require is alphabetical might help them find the problem. Otherwise people think it is broken |
The problem is using those wildcards in the first place. Other languages either don't have them or ban them in style guides. Adding horrible hacks on top of horrible hacks doesn't sound like a good idea. |
@oprypin That is understandable. If wildcard require is not preferred, then it should be changed in generated crystal projects because that's what every lib starts out with: #6122 (comment) I also think that if you can't require a folder, you should be able to require from the project root, otherwise you get into a convoluted mess of relative requires. But this is likely better in a separate discussion. Personally, I like the wildcard require and am fine with requiring a file here and there when needed, it's just not clear when you first run into it why it isn't working. Once you know it's fairly simple. In the meantime, I think we could go with the simpler solution of mentioning the require is alphabetical if it can't find a constant and you're using a wildcard. It seems that would be fairly simple change and would resolve most of the issues. Thoughts? |
Maybe we should remove wildcards from |
I certainly support that but fear the community outcry |
I definitely like wildcard Also worth noting that I've had far more problems with relative requires than with wildcard ones. Missing one level of |
Wildcard is bad because of the reasons mentioned here, but also because if we ever want to implement an incremental compilation, the I feel this language is "let's type less" oriented, and nothing else... that's not a good philosophy for a language. |
You know what? The problem is not even the wildcards. Your file actually depends on every file in that directory? Sure, knock yourself out. But, that does not exempt you from declaring dependencies in those files. The solution to everything is very simple. You need to use something? You Surely, if you write a file without any
Disgusting |
Yup, that's my suggestion too. If we want to make this strict, let's change |
The problem with an import system is that currently there is too much reliance on "re-exporting" files into a top-level file meant to be Honestly, if the suggested warning worked well, it would be a good step towards discouraging wildcards as well. But there are many problems with the implementation, both false negatives and false positives. Wouldn't most codebases have a wildcard somewhere -- then how do you know that it's the wildcard in "your" code that caused the breakage? |
@oprypin your communication is extremely off-putting and does not follow the guidelines in https://github.com/crystal-lang/crystal/blob/master/CODE_OF_CONDUCT.md#our-standards. This is not the only issue this has happened. Please try to show empathy, understand where others are coming from (even if they are wrong/misinformed!) and use less abrasive language. To address your point, whether it is disgusting or not, a large portion of people coming to Crystal is used to that. If requiring with a wildcard is in the language, I think having a hint for people is worth adding. If it's an easy change (just printing "requires are alphabetical", not saying what to require), it might be worth adding while we discuss whether to remove wildcard requires completely. If wildcard requires are removed then, of course, the message doesn't matter. @asterite I don't think it is a less typing language, at least not in my viewpoint. For me, it is about focusing on what matters to my domain by removing things I need to know and focus on. Requiring things isn't necessarily helpful. In Elixir you do not ever need to require anything (with the exception of modules that have a macro). I realize that won't work in Crystal, but the point is that the language is conceptually simple for the programmer. You define a module, you use it. That's it. You can alias things and import them to make calling them shorter, but that is your choice. It isn't required. I can see how removing wildcard can be simpler in some sense, but in its current implementation, it is not. It is tied too tightly to the structure of your folders, making it a pain to use. If you decide to move things to another subfolder, you have to change all your requires. You also have to use a different require based on where you're calling from. If wildcards are removed I think there should be something similar to webpack where you can set a resolver path so that tiny changes to directory structure aren't so painful. It also makes it easier to reference files because you use the same require in every file instead of changing it based on where the file currently is. Alternatively, having a wait to require from the working directory would be nice. Thoughts? In closing, if requiring wildcards won't be removed for a few months (that would be my guess since it would require a lot of changes to a lot of libraries to have a smooth transition), then is it worth adding a simple message (just the note about alphabetical requires) to help people in the meantime? If not, then I think we should close this and open another issues about removing wildcard requires |
I can't see a nice way to implement this. The compiler sees: include Mailer and it can't find it. Where is it defined? You know it's in a |
@asterite What about the simpler version I suggested where it doesn't try to be too smart? I image it would work something like this for the simpler version:
These type of messages are in Lucky and have been received well. It doesn't try to be too smart, and it is just helpful enough to point people in the right direction |
And if there were the more complex version I originally suggested, I think it would be fairly naive:
No parsing the files or anything crazy. It might not find everything, but it doesn't have to. Just like Note: I'd be happy to implement if given some brief pointers on where to hook into. I don't have much experience with the compiler |
@paulcsmith Nice issue! 😉 I think a simple message like you commented here #6122 (comment) would be good enough 👍 |
Sure, if someone wants to implement this, go ahead. |
@asterite I'd be happy to! Could you give me a pointer on where to hook into? I imagine I'd hook in where the compiler decides whether to show "Did you mean?" for constants. crystal/src/compiler/crystal/semantic/call_error.cr Lines 23 to 37 in df279d1
Is there a way to get at the path for the file where the compile error occurred? This would be useful for the more complex case if I decide to really stretch a bit :) |
Path, like any other ast node, has a location property. Location is a bit tricky because the file might refer to an expanded macro, but you can get the location where the macro was expanded. But note that undefined constant might happen in other places too, I think. |
@asterite That's a great starting point. Thank you! |
I think,
The former issue never mentioned these two simple points. |
One aspect I haven't seen mentioned here is tooling. When trying to use tooling (for example macro expansion), you you have do a command line like This make me somewhat wonder if having a declartion driven solution would be neater - at least for the dependencies within a project. For dependencies to other shards require works fine and is quite free from tedium. There are some order dependencies here - open classes and whatnot but perhaps that could be solved, and I also don't know how to couple this with different requirement structures for different compile target sets (and having multiple targets is the norm as the spec suite is also a separate target). I see this as mostly a conflict about good ways to handle a project and good ways to handle single files being compiled. Extra nice if it was possible to tell the compiler to expand a macro in a file from any compile target in the current project. |
I'm in support of a more elaborated error message for missing constants. Any magic beyond that I don't see as realistic. Having the compiler to pseudo require and parse all files it can find under some magic directory (right now |
This will fail to find the constant
Mailer
when you runcrystal src/send_email.cr
because the files are loaded alphabetically. SinceInvitationEmail
is loaded before the requiredMailer
dependency it fails. This may be unexpected for newcomers if they are usingrequire "*"
and it works fine until they happen to have a dependency that due naming is not loaded yet.I think it would be helpful for the compiler to check the
src
folder for unrequired files that have a class/module that matches and tell the user what to require to get it working. In this case:It wouldn't find everything (for example if you forgot to load "colorize" and tried to use the
colorize
method), but I think this would help a lot for some of the common cases.Note, I am not saying the compiler actually compiles the unrequired file, it just does a simple text search looking for the constant in
.cr
files so it can see if you forgot to require one.Challenges
Constants nested in modules like this would be easy to find:
But I imagine this would be harder
Different solutions?
I'm not sure this is the best solution, but this is definitely a problem I've seen a number of times that trips people up. I'd love to hear alternatives if this won't work or would be too hard
From #6122 (comment)
The text was updated successfully, but these errors were encountered: