From: "fxn (Xavier Noria) via ruby-core" Date: 2025-02-14T07:15:24+00:00 Subject: [ruby-core:121040] [Ruby master Misc#21035] Clarify or redefine Module#autoload? and Module#const_defined? Issue #21035 has been updated by fxn (Xavier Noria). OK, thanks for taking the time to think about this @mame. I can explain the use case in Zeitwerk, but I think I understand what you have in mind. Problem is, it is difficult for me to see it because documentation does not cover this. Zeitwerk basically scans the file system to set autoloads dynamically. If it finds a top-level file `foo.rb`, it defines ``` ruby autoload :Foo, '/full/path/to/foo.rb' ``` That is dynamic, no source code involved. And AFAIK I have no way to know if a given file is in the stack of features being loaded. Then, some logic needs to know if `Foo` has an autoload, but I cannot just with the API. Because of the way this works today, after a dynamic `autoload` call I need to invoke `autoload?` and register the constant path in a special collection. Then, my own "autoload?" logic is `autoload? || registered?", conceptually. But once you said `Module#constant` should agree with the predicates, I believe I see our different point of views. The way I think about autoload is, very briefly: 1. `Module#autoload` defines a file to be require when the constant is looked up in the receiver. 2. `Module#autoload?` says whether there is an autoload for the argument. 3. On constant lookup, an autoload is ignored if the target file is present in the stack of features being loaded. Could it be that you think about this in this manner? 1. `Module#autoload` defines a file to be require when the constant is looked up in the receiver, unless the target file is in the stack of loaded features. 2. `Module#autoload?` says whether there is an autoload for the argument, unless the argument is _now_ in the stack of features being loaded. Emphasis in _now_, because maybe the file was not in the stack when the autoload was set, but it is later when the predicate is checked. And `constants` and `const_defined?` would document what is consistent with each option (in my version, their output is trivial, it is metadata, in the second version, all of them run a validation.) Now, if the Ruby team prefers the second version and we fix `Module#constants`, then I'd be OK with the resolution (would not agree, but it is your call :). But then, as I said in the ticket description, I think all this semantics should be precisely documented. ---------------------------------------- Misc #21035: Clarify or redefine Module#autoload? and Module#const_defined? https://bugs.ruby-lang.org/issues/21035#change-111948 * 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/