-
-
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
No indication of which method is used when two libraries have the same method in a re-opened class. #12151
Comments
Hi! Redefining a method with another one is the language's nature. It works like that in Ruby too. And, like in Ruby, you get no indication about when that happens. I'm... not sure there's anything actionable here. |
Of course a solution to this is to just not randomly monkey patch common types like that. Maybe there would be room for introducing the Definitely not saying we should make common types like |
Crystal considers the entire codebase a single unit and there is no compartmentalization between code from different libraries, or stdlib. To the compiler, it's all the same and it can't really tell whether something defined in location A conflicts with the same thing in location B. Of course, the compiler knows when a method definition shadows another one. But that's normal and often expected behaviour. It shouldn't cause a warning. |
I came to think of a practical enhancement that would make it easier to deal with such issues. Note that this is not just about different shards implementing a method with the same name, but also overloads of a method in the same library with different signatures etc. When it's a complete shadowing like in the example case, the actual target def can be determined statically. When there are multiple matching defs which are dispatched at runtime, it gets even more complicated. You might be looking for bugs in other parts of the implementation before you realize that perhaps the call goes to some other def. And even when you've realized that, it can be tricky to figure out which def actually gets called. The $ crystal tool implementations src/main.cr -c src/main.cr:4:7
1 implementation found
/home/johannes/src/crystal-lang/crystal/reopened-class-example/lib/shard1/src/shard1.cr:2:3 For cases such as this example, it can be a big help. I can easily see which def gets called and can jump to the location. That should be enough to get you going. But this tool is far from easy to use. It would be really nice if we could have a macro for this, which would make it super easy to use when you just put it in the code at the call you want to inspect: tell_me_where_this_ends_up "".someMethod # => /home/johannes/src/crystal-lang/crystal/reopened-class-example/lib/shard1/src/shard1.cr:2:3 This should be easy to do, the code already exists and should be possible to hook into the macro language. Inspecting implementations at compile time would be super nice. Now with dynamic dispatch, it is much more complicated to figure out the actual target def. We'd need some enhanced run time reflection for that. For native code, I'm not sure if we could achieve anything with reasonable effort. A solution that might be relatively easy to do would be injecting some instrumentation into the target defs which only becomes active after. module Crystal::Reflection
class_property bleep = false
def self.bleep(deff, location)
if bleep
puts "#{deff} #{location}"
end
bleep = false
end
end
macro tell_me_where_this_ends_up(call)
Crystal::Reflection.bleep = true
{{ call }}
end
def foo(x : Int32)
Crystal::Reflection.bleep("foo(x : Int32)", "#{__FILE__}:#{__LINE__ - 1}") # injected by compiler
end
def foo(x : String)
Crystal::Reflection.bleep("foo(x : String)", "#{__FILE__}:#{__LINE__ - 1}") # injected by compiler
end
tell_me_where_this_ends_up foo(1 || "") # foo(x : Int32) /home/johannes/src/crystal-lang/crystal/.test/tell_me_where_this_ends_up.cr:34 |
Well my main concern was just that it could, potentially, be quite confusing on things that aren't trivial. I am definitely not a Ruby programmer, and only started tinkering with that language after discovering Crystal, so this behavior is honestly new to me ^_^ As Straight-Shoota said:
Although my example used the String class in the stdlib, I wasn't intending it to be strictly interpreted as that case. A different example might be if I have my own class and a method on it updates an instance variable, but some other library overrides that method (and does not update the instance variable). To use a more concrete example, I was considering extending the IO class in my own personal standard lib with some convenience methods. That's when I thought, "What if some other shard I use does the same thing?" And that's what got me thinking about this. I'm used to seeing a simple style warning in other languages when a method/function is re-defined in a separate file. But, I honestly think even just a mention in the language docs that the order of |
This is partially why you should namespace your library's types. E.g. |
Somewhat related discussion #8422 |
Let's say I have a program that depends on two Shards,
Shard1
andShard2
. InShard1
, the String class is reopened and a new method calledsome_method
is added.Shard2
has the same thing, a re-opened String class with a method calledsome_method
. The order that Irequire
the shards seems to dictate which version ofsome_method
is used.I didn't find anything about this in the docs, or a previously opened issue, but my searching skillz may be a bit lacking 😅
What aspect of the language would you like to see improved?
A warning/note message stating that one library overrode the method introduced in another library would be helpful, or explicit documentation about this. Or both.
What are the reasons?
If I wasn't aware that this depended on the order of
require
s, in a complex program, this could be quite confusing without indication of which method the compiler is choosing.Include practical examples to illustrate your points.
There's a very small project to illustrate this here: https://gitlab.com/RemiliaScarlet/reopened-class-example
The text was updated successfully, but these errors were encountered: