From: "Eregon (Benoit Daloze) via ruby-core" Date: 2025-02-14T15:14:33+00:00 Subject: [ruby-core:121049] [Ruby master Misc#21035] Clarify or redefine Module#autoload? and Module#const_defined? Issue #21035 has been updated by Eregon (Benoit Daloze). fxn (Xavier Noria) wrote in #note-15: > I think `Module#constants` and `Module#const_defined?` should match. I believe we agree on that one, right? I'm not sure ���� IMO an autoload which does define the constant correctly should stay in `Module#constants` at all times from the `autoload :Foo, "path"`, on all threads. It seems too weird for a constant to disappear from `Module#constants` because it's being autoloaded by the current thread, and could cause subtle problems. IOW, I think `Module#constants` is correct as it is. I do realize that with `Module#const_defined?` and `defined?` and `autoload?` the constant looks like it disappears while it's being autoloaded by the current thread. (that's the inconsistency you mention in the description) I think for `Module#const_defined?` and `defined?` there might be valid reasons for it, notably for that pattern in https://bugs.ruby-lang.org/issues/21035#note-9. For `autoload?` I don't see any reason to make it disappear and so complicated behavior. I think `autoload?` should just query what's in the constant table: if it's an autoload constant and it's not resolved yet it should return the autoload path, whether it's currently loading or not, on all threads. --- > should print "foo" unconditionally, I just registered the autoload! Yes, I think we can agree this is surprising behavior. Maybe we can agree it is undesired behavior, WDYT @mame? It is unfortunately rather brittle when e.g. defining `autoload :Foo, "foo"` that if there is any `foo.rb` under `$LOAD_PATH` that is being required currently it's considered to be the file defining constant `Foo`, which it may or may not be. Although in theory such conflicts should not happen much, because gems/apps are supposed to have different top-level namespace and e.g. `module A; autoload :Foo, "a/foo"; end` does not conflict with `module B; autoload :Foo, "b/foo"; end`. @fxn Do you have an issue documenting how the conflict in autoload paths can happen in the real world from that 2019 issue or more recent? @fxn How do you workaround that strange behavior in Zeitwerk, could you point to some code? ---------------------------------------- Misc #21035: Clarify or redefine Module#autoload? and Module#const_defined? https://bugs.ruby-lang.org/issues/21035#change-111961 * Author: fxn (Xavier Noria) * Status: Open ---------------------------------------- The documentation for `Module#autoload?` says: > Returns filename to be loaded if name is registered as autoload in the namespace of mod or one of its ancestors. As a user, if I declare an autoload, I expect this API: ```ruby module M autoload :Foo, 'foo' constants # => [:Foo] const_defined?(:Foo) # => true autoload?(:Foo) # => 'foo' end ``` That it is indeed how it generally works. Even if the autoload path does not exist. But there is an edge case. While `constants` does include always `:Foo` as far as I can tell, the return value of `const_defined?` and `autoload?` depends on the stack of features being loaded: The autoload path is resolved and if seen to be in the stack of features being loaded, the predicates return `false` and `nil`, respectively. Do you think that is intuitive? I find that logic totally unexpected. I just defined an autoload, therefore, I think it would be natural for `autoload?` to return what I just configured. Why should `const_defined?` return nothing but `true`? And why is it not consistent with `constants`? To me, it would make more sense that in the previous example `const_defined?` returns `true`, and `autoload?` returns `foo` unconditionally (and instantly, nowadays it takes a relative long time due to the lookup). Now, if the autoload is triggered in a lookup **then** I would expect `Kernel#require` logic to apply. But not when calling some simple predicates. Please, note that the present behavior is not documented, so on paper the change would not be backwards incompatible. If, on the other side, it is preferred to keep the behavior as it is, I guess it should be documented with precision (accounting for symlinks, relative paths in `$LOAD_PATH`, etc.) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/