From: "shioyama (Chris Salzberg) via ruby-core" Date: 2023-05-14T02:24:57+00:00 Subject: [ruby-core:113469] [Ruby master Feature#19633] Allow passing block to `Kernel#autoload` as alternative to second `filename` argument Issue #19633 has been updated by shioyama (Chris Salzberg). > Let's talk anonymous namespaces for a moment in Zeitwerk. If you could assign an anonymous namespace to `/foo` in Zeitwerk, what would go in the top-level `class` or `module` keyword in `/foo/baz.rb`? I think this is the key point where we may have differing views. Not strongly differing perhaps though. I think `/foo/baz.rb` should just have a normal structure, i.e. something like e.g. ```ruby module Foo class Baz end end ``` Im will load this under an anonymous namespace if you set up paths accordingly. I believe you would want a "design based on explicit cooperation" to use your earlier quote, in which a the file level itself you would opt-in to being autoloaded under an anonymous namespace. I personally do not believe this is necessary. When I wrote "if Zeitwerk were to support the same funcationality", I mean I would want to be able to (say) create an alternative loader, which like Im subclasses `Module`, and which I could assign to paths just like in Zeitwerk. Except that these paths would be loaded under the loader instead of at top-level. Nothing else would change in the files themselves. Im is exactly what I am imagining, no more no less. I am not married to this vision, and happy to discuss further, but this is where I am coming from. Personally I feel that "autoloading to anonymous modules" should be the more generic case here, and "autoloading to top-level" the specialized version of that. Of course in practice Ruby is not designed to make the former easy (although much easier as of 3.2!), but that to me is simply an implementation detail. Also, related to this, I gave a talk at RubyKaigi the other day (in Japanese) about this ([slides](https://speakerdeck.com/shioyama/multiverse-ruby)) and there was a lot of positive response. I feel there is actually a lot of people interested in this topic, who in the past have simply seen this as impossible. It is now no longer impossible, and this is sparking a lot of interesting discussions (like this one here! :) ). ---------------------------------------- Feature #19633: Allow passing block to `Kernel#autoload` as alternative to second `filename` argument https://bugs.ruby-lang.org/issues/19633#change-103048 * Author: shioyama (Chris Salzberg) * Status: Open * Priority: Normal ---------------------------------------- `Kernel#autoload` takes two arguments, a symbol `module` representing the constant to be autoloaded, and a `filepath` to load the constant from (with `require`). Currently, Zeitwerk has to [monkeypatch `Kernel#require`](https://github.com/fxn/zeitwerk/blob/a7c4a983df0f4e4058f32c610dac1e8b99f687da/lib/zeitwerk/kernel.rb) to fetch the loader for the file being loaded, then run the original (aliased) `require`, then run autoload callbacks. In addition to the monkeypatch, this also requires a registry (`Zeitwerk::Registry`) to map file paths to loaders, to know which loader should be used for a given autoload-triggered `require`. In fact, Zeitwerk has to _assume_ that the monkey-patched `require` call came from an autoload trigger; there is no way to really be sure of the source. If Ruby allowed passing a block as an alternative to the explicit filepath, then I think this could be improved and would eliminate the need for a monkeypatch. So something like this: ```ruby autoload(:B) do require "lib/b" # trigger callback, etc end ``` I am implementing a gem called [Im](https://github.com/shioyama/im) which is a fork of Zeitwerk, and in the case of this gem, such a feature would be even more useful. Im implements autoloads on anonymous modules by registering an autoload and then "catching" the require and converting it into a `load`, passing the module as the second argument (see [here](https://github.com/shioyama/im/blob/44ce348639a1aae563a5be7a40602761e9698d43/lib/im/kernel.rb).) This is currently quite tricky because, again, it's hard to know _where_ a `require` came from. In addition to removing the monkeypatch (inherited from Zeitwerk), Im would further benefit from the block argument because then it could simply access the module via a closure, rather than pulling it from a registry: ```ruby mod.autoload(:Foo) do load "lib/foo.rb", mod end ``` I don't know how hard or easy this would be to implement, but if there is interest in this as a feature I'd be happy to look into implementing it. -- 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/postorius/lists/ruby-core.ml.ruby-lang.org/