How are perl5’s BEGIN blocks equivalent to comptime? It’s been awhile, but I recall BEGIN blocks executing at require time—which, in complicated pre-forking setups that had to be careful about only requiring certain modules later during program execution because they did dumb things like opening connections when loaded, meant that reasoning about BEGIN blocks required a lot more careful thought than reasoning about comptime.
The same is true for templates, or macros—all of which are distinguished by being computed in a single pass (you don’t have to think about them later, or worry about their execution being interleaved with the rest of the program), before runtime start (meaning that certain language capabilities like IO aren’t available, simplifying reasoning). Those two properties are key to comptime’s value and are not provided by perl5’s BEGIN blocks—or probably even possible at all in the language, given that it has eval and runtime require.
BEGIN blocks execute at compile-time. require is just a wrapper to load a module at compile-time.
When you want to use state, like openening a file for run-time, use INIT blocks instead. These are executed first before runtime, after compile-time.
My perl compiler dumps the state of the program after compile-time. So everything executed in BEGIN blocks is already evaluated. Opening a file in BEGIN would not open it later when required at run-time, and compile-time from run-time is seperated.
All BGEIN state is constant-folded.
I think we’re using different definitions of “compile time”.
I know who you are, and am sure everything you say about the mechanisms of BEGIN is correct, but when I refer to “compile time”, I’m referring to something that happens before my program runs. Perl5’s compilation happens the first time a module is required, which may happen at runtime.
Perhaps there’s a different word for what we’re discussing here: one of the primary benefits of comptime and similar tools is that they are completed before the program starts. Scripting languages like perl5 “compile” (really: load code into in-memory intermediate data structures to be interpreted) at arbitrary points during runtime (require/use, eval, do-on-code).
On the other hand, while code in C/Zig/etc. is sometimes loaded at runtime (e.g. via dlopen(3)), it’s compile-time evaluation is always done before program start.
That “it completed before my code runs at all” property is really important for locality of behavior/reasoning. If the comptime/evaluation step is included in the runtime-code-load step, then your comptime code needs to be vastly more concerned with its environment, and code loading your modules has to be vastly more concerned with the side effects of the import system.
(I guess that doesn’t hold if you’re shelling out to compile code generated dynamically from runtime inputs and then dlopen-ing that, but that’s objectively insane and hopefully incredibly rare.)
The same is true for templates, or macros—all of which are distinguished by being computed in a single pass (you don’t have to think about them later, or worry about their execution being interleaved with the rest of the program), before runtime start (meaning that certain language capabilities like IO aren’t available, simplifying reasoning). Those two properties are key to comptime’s value and are not provided by perl5’s BEGIN blocks—or probably even possible at all in the language, given that it has eval and runtime require.