7 ms·
> One (maybe the) distinguishing feature between comptime in Zig and Rust macros seems to me to be access to type information. In Zig you have it[1] in Rust you
by pron 2y ago
> One (maybe the) distinguishing feature between comptime in Zig and Rust macros seems to me to be access to type information. In Zig you have it[1] in Rust you don't and that makes a big difference.
There are other differences. First, comptime functions aren't syntactic macros. This makes them much easier to reason about and debug. You could think about them as if they were regular functions running at runtime in a partially-typed language with powerful reflection (their simplicity also means they're weaker than macros, but the point is that you can get very far with that, without taking on the difficulties associated with macros). Second, I think that comptime's uniqueness comes not from what it does in isolation, but that it makes other language features redundant, keeping the entire language simple. This means that with one simple yet just-powerful-enough feature you can do away with several other features.
The end result is that Zig is a very simple language with the expressivity of far more complicated languages. That on its own is not super unusual; in a way, JavaScript is like that, too. But Zig does it in a low-level language, and that's revolutionary. It is because of its simplicity that people compare Zig to C, but it's as expressive as C++ while also being safer than C++, let alone C.
Adding comptime to an already-complex language misses out on its greatest benefit.
- forks 2y agoWhat are some examples of other language features that comptime makes redundant?
- dhruvrajvanshi 2y agoIt's generic system for example, is built on top of comptime. A generic struct is just a function that takes a type as an argument and returns a struct. ``` fn Vec(comptime T: anytype) { return struct { // ... } } ``` IMO having a first class generic type parameter syntax is better but this demonstrates OP's point.
- Wumpnot 2y agoIt just looks like C++ templates with a slightly different syntax ..
- azakai 2y agoExactly, the point is that C++ added templates as a huge new language feature, while in Zig it is just one of the things that is immediately possible thanks to comptime.
- throwawaymaths 2y agowell, not quite since you can pass non-type things of generally any level of data type complexity (as long as it's comptime-valid, which only excludes certain types of mutation), and do stuff with them that you couldnt in c++. fn MyType(T: type, comptime tag: [] u8, comptime count: usize) type { const capitalized = some_module.capitalize(tag); return struct{ fn name() []const u8 { return capitalized; } array: [count]T, }; } for example
- Wumpnot 2y agoThat example looks easy enough to replicate in C++ with consteval + template, basically the same except a few minor syntax changes.
- Maxatar 2y agoYou absolutely can't do that in C++ with consteval + template. C++ would need support for reflection to do that, and maybe it will get it in 10 years, maybe not, but as of today this would not be possible. Furthermore, the original argument wasn't about whether something can or can't be done in C++, it was that this one feature in Zig subsumes what would require a multitude of features from C++, such as consteval, templates, SFINAE, type traits, so on so forth... Instead of having all these disparate features all of which work in subtly different ways, you have one single feature that unifies all of this functionality together.
- 2y ago
- SkiFire13 2y agoNit: comptime does not replace a proper generic system (i.e. a polymorphic type system), but acts more like a templating system (like the one in C++).
- pron 2y agoGenerics, interfaces/traits/concepts, macros, conditional compilation, const functions/constexpr. These are four or five different features in C++ or Rust, some of which are quite complex, all expressible as one simple construct: comptime.
- kobebrookskC3 2y agohow does zig express a trait like Send https://doc.rust-lang.org/std/marker/trait.Send.html https://doc.rust-lang.org/std/marker/trait.Send.html which ensures that values can safely be moved to another thread, for example when spawning a thread? https://doc.rust-lang.org/std/thread/fn.spawn.html https://doc.rust-lang.org/std/thread/fn.spawn.html
- throwawaymaths 2y agoif you want that you will need a proof checker or something.
- pcwalton 2y agoSuch as the one Rust provides, and that Zig cannot, due to not being memory safe.
- rc00 2y agoIt sounds like you haven't seen projects like this yet: https://github.com/ityonemo/clr https://github.com/ityonemo/clr They're popping up all over. For some reason, Zig folk want Rust things and Rust folk want Zig things.
- pcwalton 2y agoI have seen projects like that, and they're a prime example of what I mean when I want to see arguments in favor of Zig that rely on what Zig can do now, and not what it could potentially do in the future. Ten years ago, C++ folks were also promising memory safety with the ISO C++ Core Guidelines; you don't hear much about that anymore, because it turns out it can't be done while keeping the resulting language C++ (for example, seanbax's work for C++, which is far more advanced than this project, is really awesome, but is essentially a different language).
- creata 2y ago> This makes them much easier to reason about and debug. Can you give an example of something that's easier to reason about (e.g., an error that's easier to spot) with Zig's comptime than with macros? > it makes other language features redundant I'm guessing (so I might be wrong) that IDEs and users still need to be aware of the common idioms, so why does it matter whether or not those common idioms are implemented in the compiler or using comptime? (I'm not saying it doesn't matter, I'm wondering what benefits you have in mind.)
- WhyNotHugo 2y ago> Can you give an example of something that's easier to reason about (e.g., an error that's easier to spot) with Zig's comptime than with macros? Rust proc_macros takes a stream of tokens and return a stream of tokens. If your macro meant to return an instance of a specific type, it must output the correct tokens which create that instance via existing interfaces. There's some really ugly indirection in trying to understand what's going on. This is always harder to reason about than Zig's equivalent, because in Zig you just return the thing that you want to return.
- NobodyNada 2y agoHow does it work if I wanted to construct a type (and maybe a set of helper types. some related functions, etc.), rather than an instance of a type? If I just wanted to construct an instance of a specific type at compile time in Rust, I'd probably be using a const fn instead of a macro.
- cgh 2y agoYou return the type directly. You can then declare things to be of this type. Eg, from the Zig docs, here's how to construct a generic List type (note the comptime declaration of the generic parameter): fn List(comptime T: type) type { return struct { items: []T, len: usize, }; } // The generic List data structure can be instantiated by passing in a type: var buffer: [10]i32 = undefined; var list = List(i32){ .items = &buffer, .len = 0, };
- GrantMoyer 2y agoNit-pick: Javascript is not simple; I personally think it has among the most complex language semantics out of all commonly used languages.
- HelloNurse 2y agoJavascript would be a simple hybrid of object oriented and functional principles if backward compatibility with old hacks and "robust" use for web page scripting didn't require a host of redundant features, syntactic bizarre special cases and and evil semantic choices: -- comments, iterating objects and arrays, the absurd equality operators and type conversions, and so on.
- efnx 2y agoPrototypal inheritance can be pretty awkward and complicated. It’s evident in that few young JS devs ever use it, and many don’t even know about it!
- chuckadams 2y agoI'm an old JS dev, having been around from its beginning, and I avoid JS's OOP system of prototypes too (along with the class syntax) and stick to only static closures in object literals. VueJS and TypeScript are also mostly written in that style, so I'm in good company. I'm tickled pink that we're at the point where a dev actually _can_ forget that the OOP system exists, because its a horror show. I learned OOP on a prototype-based system (LambdaMOO) so it's not like it's foreign to me, JS's system is just so full of warts and wats it's not funny. It's scary how good TS is at inferring the type, it'll even infer an instance of a class from a literal, so the interop with class-based code is almost seamless (almost: just don't try destructuring a "real" object or re-binding `this` on a static closure)
- Lerc 2y agoCan you give an example outside of the weirdness caused by automaticy converting types? (Or 'with', which is kind-of in purgatory now) I have considered a non backwards compatible JavaScript descendant to clean things up. It would be interesting to hear what you consider to be problems.
- huijzer 2y agoI always on purpose avoid macros and comptime as much as possible because they usually make code much harder to reason about and to debug. Also, often macros are hard to refactor. Would you agree with my idea, or would you say I am missing something? Does Zig aleviate some of the problems I mentioned?
- vijaybritto 2y agoNow that I'm older I agree with you, but I would have been furious hearing this ten years ago :)
- pjmlp 2y agoZig's safety is kind of on par with Modula-2, and Object Pascal, which are equally expressive as high level languages, with low level capabilities, but apparently curly brackets have won. The two things it adds on top, is the way nullability is handled at compile time, while the former do runtime null checks, and comptime.
- baranul 2y ago> curly brackets have won True, but at least there are curly bracket languages heavily influenced by Pascal, such as Go, V (Vlang) (has comptime too and is debatably safer), Odin, etc...