4 ms·
D has had compile time function execution since 2007 or so. https://dlang.org/spec/function.html#interpretation https://dlang.org/spec/function.html#interpreta
by WalterBright 1y ago
D has had compile time function execution since 2007 or so.
https://dlang.org/spec/function.html#interpretation https://dlang.org/spec/function.html#interpretation
It doesn't need a keyword to trigger it. Any expression that is a const-expression in the grammar triggers it.
- jhgb 1y agoBut Zig doesn't need a keyword to trigger it either? If it's possible at all, it will be done. The keyword should just prevent run-time evaluation. (Unless I grossly misunderstood something.)
- WalterBright 1y agoI'm no expert on Zig, but "comptime" is the keyword to trigger it.
- jhgb 1y agoI'm pretty sure the "comptime" keyword only forces you to provide an argument constant at compile time for that particular parameter. It doesn't trigger the compile time evaluation.
- WalterBright 1y agoThat's how the constant is provided - through compile time evaluation.
- jhgb 1y agoYes, but compile-time evaluation in Zig doesn't require the "comptime" keyword. Only specific cases such as compile-time type computation do (but these specific cases are not provided by compile-time function evaluation in D anyway, so language choice wouldn't make a difference here).
- rurban 1y agoPerl5 had it before. Either by constant-folding, or by BEGIN blocks. Constant-folding just got watered down by the many dynamic evangelists in the decades after, that even C or C++ didn't enforce it properly. In perl5 is was watered down on add (+) by some hilariously wrong argumentation then. So you could precompute mult const expressions, but not add.
- zbentley 1y agoHow 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.
- rurban 1y agoBEGIN 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.
- zbentley 1y agoI 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.)
- brabel 1y agoYes and D's comptime is much more fun, IMHO than Zig's! Yet everyone talks about Zig's comptime as if it were unique or new.
- deleted 1y ago[deleted]
- tialaramex 1y agoMaybe I don't understand, in D, how do I write a function which makes a new type? For example Zig has a function ArrayHashMapWithAllocator which returns well, a hash table type in a fairly modern style, no separate chaining and so on Not an instance of that type, it returns the type itself, the type didn't exist, we called the function, now it does exist, at compile time (because clearly we can't go around making new types at runtime in this sort of language)
- pjmlp 1y agoYou use templates and string mixins alongside each other. The issue with mixins is that using string concatenation to build types on the fly isn't the greatest debugging experience, as there is only printf debugging available for them.
- WalterBright 1y agoSee my other post.
- ScottRedig 1y agoHello Mr. Bright. I've seen similar comments from you in response to Zig before. Specifically, in the comments on blog post I made about Zig's comptime. I took some time reading D's documentation to try to understand your point (I didn't want to miss some prior art, after all). By the time I felt like I could give a reply, the thread was days old, so I didn't bother. The parent comment acknowledges that compile time execution is not new. There is little in Zig that is, broad strokes, entirely new. It is in the specifics of the design that I find Zig's ergonomics to be differentiated. It is my understanding that D's compile time function execution is significantly different from Zig's comptime. Mostly, this is in what Zig doesn't have as a specific feature, but uses comptime for. For generics, D has templates, Zig has functions which take types and return types. D has conditional compilation (version keyword), while Zig just has if statements. D has template mixins, Zig trusts comptime to have 90% of the power for 10% of the headache. The power of comptime is commonly demonstrated, but I find the limitations to be just as important. A difference I am uncertain about is if there's any D equivalent for Zig having types being expressions. You can, for example, calculate what the return type should be given a type of an argument. Is this a fair assessment?
- WalterBright 1y ago> A difference I am uncertain about is if there's any D equivalent for Zig having types being expressions. You can, for example, calculate what the return type should be given a type of an argument. This is done in D using templates. For example, to turn a type T into a type T star: template toPtr(T) { alias toPtr = T*; } // define template toPtr!int p; // instantiate template pragma(msg, "the type of p is: ", typeof(p)); The compiler will deduce the correct return type for a function by specifying auto* as the return type: auto toPtr(int i) { return cast(float)i; } // returns float For conditional compilation at compile time, D has static if: enum x = square(3); // evaluated at compile time static if (x == 4) int j; else double j; auto k = k; Note that the static if* does not introduce a new scope, so conditional declarations will work. The version is similar, but is intended for module-wide versions, such as: version (OSX) { stuff for OSX } else version (Win64) { stuff for Windows 64 } else static assert(0, "unsupported OS"); Compile time execution is triggered wherever a const-expression is required. A keyword would be redundant. D's mixins are for generating code, which is D's answer to general purpose text macros. Running code at compile time enables those strings to be generated. The mixins and compile time execution are not the same feature. For a trivial example: string cat(string x, string y) { return x ~ "," ~ y; } string s = mixin(cat("hello", "betty")); // runs cat at compile time writeln(s); // prints: hello,betty I'll be happy to answer any further questions
- laidoffamazon 1y ago[flagged]
- jadbox 1y agoHi Walter! Big fan. What do you think of Zig? How would you like to see it evolve? Are there any things from Zig that inspire you to work in D?
- WalterBright 1y agoI have never written a Zig program, I've just browsed the specification. I do admire the energy and enthusiasm of its creators. The fast compiles of it are well done. Mostly what I think is the syntax is more complex with less utility than the equivalent D syntax. For example, the use of the 'comptime' keyword is not necessary. For another, the import declaration is overly complex. I don't know enough about Zig to make informed suggestions on evolving it. D has borrowed stuff from many languages, but I don't recall suggestions in the D forums of a Zig feature that should be added to D, though I might have missed it.
- pron 1y agoPartial evaluation has been quite well known at least since 1943 and Kleene's Smn proof. It has since been put to use, in various forms, by quite a few languages (including C++ in 1990, and even C in the early seventies). But the extent and the way in which Zig specifically puts it to use -- which includes, but is not limited to, how it is used to replace other features that can then be avoided (and all without macros) -- is unprecedented. Pointing out that other languages have used partial evaluation, sometimes even in ways that somewhat overlap with Zig's use, completely misses the point. It's at least as misplaced as saying that there was nothing new or special about iPhone's no-buttons design because touch screens had existed since the sixties. If you think Zig's comptime is just about running some computations at compile time, you should take a closer look.
- WalterBright 1y agoI'd like to see an example! as I cannot think of one.
- pron 1y agoAn example of what?
- WalterBright 1y agoAn unprecedented use.
- pron 1y agoOk, so a primary goal of comptime in Zig is to avoid needing certain specialised features while still enjoying their functionality, in particular, generics, interfaces, and macros. I'm not aware of any language that has been able to eliminate all of these features and replace them with a simple, unified partial evaluation mechanism. In addition, there's the classic example of implementing a parameterised print (think printf) in Zig. This is a very basic use of comptime, and it isn't used here in lieu of generics or of interfaces, but while there may be some language that can do that without any kind of explicit code generation (e.g. macros), there certainly aren't many such examples: https://ziglang.org/documentation/0.15.2/#Case-Study-print-in-Zig https://ziglang.org/documentation/0.15.2/#Case-Study-print-i... But the main point is that the unprecedented use of partial evaluation is in having a single unified mechanism that replaces generics, interfaces, and macros. If a language has any one of them as a distinct feature, then it is not using partial evaluation as Zig does. To continue my analogy to the novel use of a touchscreen in the iPhone, the simplest test was: if your phone had a physical keypad or keyboard, then it did not use a touchscreen the way the iPhone did.