From: "shioyama (Chris Salzberg) via ruby-core" Date: 2023-05-13T06:01:54+00:00 Subject: [ruby-core:113465] [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). Thanks @fxn for your reply! I was going to update this, but I brought it up at the recent [Dev Meeting](https://bugs.ruby-lang.org/issues/19599#note-14) before RubyKaigi and although there was interest, Matz seemed not to be in favour of it. I assume this will be closed. That aside, I'll hijack this thread to respond to some of your other comments which are very important to me. First of all, text can be an ambiguous medium so for the record, I would drop work on Im right now if Zeitwerk were to support the same funcationality (autoloading to anonymous namespaces). I'm not dying to maintain a fork! But at the same time, discussions about namespaces (e.g. #10320 and #19024) were getting very hypothetical, and I felt that rather than waiting for some consensus to emerge, it made sense to just forge forward to see what this world would actually look like. That's my immediate intent with Im. Longer-term, I am not sure where I am going with the project yet. > That is not true, why do you think that? We have barely talked about your project, and the few interactions that we had in Twitter were positive and encouraging from my side, no? I'm sorry, I think we have a case of miscommunication here. I very very much appreciate [your message of support on Twitter](https://twitter.com/fxn/status/1619242334067228673), and [replied so](https://twitter.com/shioyama/status/1619244150888742913?cxt=HHwWgoDSpbz-2vgsAAAA) at the time. When I wrote "not in favour of what I'm doing with autoloads on anonymous modules", I'm referring to [this sentiment](https://bugs.ruby-lang.org/issues/19024#note-9): > This is all very misaligned with Ruby, in my opinion. Indeed, for me, `load file, mod` is a very edge interface that in my opinion should not be pursued further because it breaks a fundamental assumption in all existing code: That by looking at your source code, you know the nesting. I have always thought it has few use cases, and they involved cooperation caller/callee. Your following sentence proposes a "design based on explicit cooperation", which perhaps includes what Im is doing in some slightly different way. But my takeaway from the quote above is that using `load file, mod`, particularly in a `Kernel#require` monkeypatch (which is what Im does), would not be something you would want to do in Zeitwerk, and further that it is not something that you think should be done. I apologize if I misread the intent, but this seems to be a natural reading of this statement ("misaligned with Ruby" is to me quite a strong statement). And I would completely understand that opinion! But for me, this is the only way I see to move forward bar having some changes to Ruby itself, which (aside from small changes like the one here) I'd prefer to avoid. > It cannot be in a constant, is it going to be in a global variable? Who does that? As in Im, not _all_ top-level namespaces are being abandoned. The goal is simply to make it _possible_ to load to anonymous-rooted namespaces. So the global here is the registry in `Im` itself, which _is_ top-level. So technically-speaking everything is still "global". But there is a huge difference IMO between a top-level namespace, and a module stored in a registry mapped to a file path. > Because knowing that things have an ordinary constant path simplifies some internal assumptions, so I validate in the boundary to be confident downwards that is the case. Completely agree with this. Again, I did not mean to criticize Zeitwerk for doing this, at all, quite the contrary it helps that this is explicit. But the [commit message](https://github.com/fxn/zeitwerk/commit/f3a43e5cf2700ac68a2e2fc508d73ffd755d749d) (which I read when I forked) is pretty clear that "Anonymous root namespaces are not supported". Since this does not say "*currently* supported", I assume this meant it would never be supported. > Who ran the `require` is irrelevant. Yes, I understand this, and my statement was slightly sloppy. Thank your for clarifying this. > If your project has some rogue `require`s, or you migrate an existing gem to Zeitwerk, and existing client code is issuing `require`s, things will work. Yes! Again, I was slightly handy-wavy in what I wrote this. I understand this, and Im inherits from this and benefits from [using the same approach](https://github.com/shioyama/im/blob/main/lib/im/kernel.rb#L34-L40). > That is not to say I am against the block, eh? Only saying I don't quite see it used by Zeitwerk at the moment. Fair enough! I don't think it will be accepted anyway, so moot point, but I appreciate your clarity. > I would prefer to not decorate `Kernel*, indeed, and I believe what would really work for Zeitwerk would be a callback associated to `Kernel#require`: If a file is required, invoke the callback with the `require` argument, and the absolute path to the file. Yes, and this was also actually brought up as an alternative at the dev meeting. I agree this would achieve the same goal for Zeitwerk. Unfortunately, for Im, this would not be helpful because I want to swap a `require` for a `load`, so not execute the `require` at all. > With this addition, the natural return value for a block argument would be the block, but a block is not a string. This is a great point, and I hadn't considered it. Although I believe this proposal will be rejected, I want to revisit this in the future and I'll keep this in mind. ---------------------------------------- Feature #19633: Allow passing block to `Kernel#autoload` as alternative to second `filename` argument https://bugs.ruby-lang.org/issues/19633#change-103044 * 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/