18 ms·
What Makes the Zig Programming Language Unique?
- socialdemocrat 4y agoCompile time code execution is implemented in a really neat way in Zig enabling anything from generics to meta programming.
- evil-olive 4y agoI wrote a Sudoku solver (my go-to "learn a new language" project) in Zig and the compile-time features were extremely useful. entirely at compile-time, I was able to generate lookup tables for "given this cell index, give me the indexes of the cells in its row, column, and box". and I simply looped over that for each board size I wanted to support, from the standard 9x9 all the way up to 64x64. another useful feature was support for arbitrary-sized ints [0] and packed structs [1]. for a 9x9 board, a cell can be represented with a 9-bit bitfield for possible values; a uint4 for the actual value; and a single bit indicating whether the value is known. Zig will happily pack that into a struct that occupies only 16 bits, and handle all the bit shifting & masking for you when you access the struct fields. this meant I was able to represent the state of a 9x9 board in just 162 bytes - a flat array of those packed structs. and the exact same code could operate on the ~28kb of state needed for 64x64. 0: https://ziglang.org/documentation/0.9.1/#Primitive-Types https://ziglang.org/documentation/0.9.1/#Primitive-Types 1: https://ziglang.org/documentation/0.9.1/#packed-struct https://ziglang.org/documentation/0.9.1/#packed-struct
- lhorie 4y agoJust out of curiosity, in your opinion what's the benefit of using a packed struct and all the generated bit fiddling compared to just a regular struct? Just better memory consumption?
- evil-olive 4y agoregular structs in Zig have an implementation-defined layout in memory - the compiler is allowed to re-order fields, add padding, etc. if you want control over the memory layout you need either a packed struct or an extern struct [0]. 0: https://ziglang.org/documentation/0.9.1/#extern-struct https://ziglang.org/documentation/0.9.1/#extern-struct
- tomcam 4y agoWith respect, I’m not sure that answered the question. Surely in a sudoku game the default layout of structs would be absolutely fine, no?
- evil-olive 4y agoahh, I might have misunderstood the question - I wasn't sure if they were asking about Zig in general or my solver in particular. yes, normal structs would have worked fine - like I said, I was learning Zig in the process, so I was going out of my way to experiment with its unique features.
- lhorie 4y agoYes, I meant in your sudoku solver, sorry, I should've been more clear.
- wisnoskij 4y agoI am not good enough of a programmer to understand all the meta/compile time programming stuff. So thank you for this actual concrete example of it being used. But why is that useful? Why regenerate a database every time you compile? Now that you have compiled the program once, can you not just delete that code and reduce your compile time to a fraction of what it was?
- skybrian 4y agoIt's useful due to convenient notation. Otherwise, you'd need to write a separate program that generates the code and call it using a separate Makefile target. (And in that case, you might check in the generated code; this is commonly done for Go packages, so that downstream dependencies don't need to run the code generator tools.) Adding a compile-time parameter to a function is considerably more convenient than writing a code generator. Zig's compile-time performance might be a problem someday, but there is likely not enough Zig code yet for this to matter. Perhaps they'll add caching if it ever turns out to be an issue.
- anonymoushn 4y agoI think it's supposed to be cached in the same manner other things are cached, but the caching does not work great for us so far (and this may be because we are stuck on stage1 due to heavy use of async). I have had comptime lookup table generation and similar stuff crash the compiler or take a long time to run. I fell back to external code generators in these cases.
- belmarca 4y agoYou rely on a compiler to produce an executable, right? Well there are many "phases" that the code goes through before becoming machine code. "Compile time metaprogramming" is having control over one of those phases available from the language itself. Exactly how it is implemented is, well, implementation and language specific. But the fact that you can do it is quite useful. You can not only write programs, you can also write programs that transform or generate programs. The full extent of what you're allowed to do is implementation-dependent. In Lisps, for example, that phase is called the macro-expansion phase (of which there can be layers of) and generally acts as a source-to-source transformation from s-exp to s-exp (possibly with side-effects). Here's an example in a Gambit Scheme REPL: > when *** ERROR IN (stdin)@8.1 -- Macro name can't be used as a variable: when > (when #t #f) #f > (pp (lambda () (when #t #f))) (lambda () (if #t #f)) So you can see the 'when' macro expands to an 'if', and that has to happen _before_ evaluation. So the REPL is in fact a REEPL (Read Expand Eval Print Loop). Very useful.
- tialaramex 4y ago> the exact same code could operate on the ~28kb of state needed for 64x64. 64x64 seems to need 64 bits for values, ie 8 bytes before we consider the known case, but there are 4096 cells. So that's surely 32kB of state immediately ? It seems to me that if we don't care to distinguish whether we "know" a value for which there is only one possibility, we can save encoding this, so the 9x9 board only needs 9 bits per cell, the 64x64 board only needs 64-bits. In this case we can answer "known?" as is_power_of_two() and we can find the known value using e.g. trailing_zeros() which is cute.
- alpaca128 4y agoI might be wrong but I think you do need to know which values were predetermined so the algorithm won't touch those cells while backtracking.
- WalterBright 4y agoFrom reading the documentation, these seem to be analogous to C bit fields with a different syntax. D has bit fields, too, using the C syntax.
- deck4rd 4y agoThat's pretty cool! What other learn-a-new-language projects do you (or others) have? I like to write a very basic HTTP 1.0 server when trying out a new language.
- hansvm 4y agoHah, I had the same learner project for Zig. I chose a different board memory layout for some increased throughput on my hardware, but generating relevant lookup masks at comptime was super beneficial with that approach too. On the topic of comptime lookup tables, transcendental discretizations are popular in graphics programming (rescale sin/cos to fit appropriately into some integer type, make an array of them to turn transcendental approximations into array lookups). Those are easy to make flawless and fast in Zig. Another thing that enables without too much song and dance is compile-time duck typing. You can write a function that easily handles a call like randint([][4]@Vector(12, u8), .{Min(0), Max(13), my_allocator, Shape(.{12345})}) which would allocate that type with an appropriate size and fill it with random integers within the prescribed bounds. It's perhaps a bit more verbose than a truly dynamic language, but comptime type introspection enables that sort of powerful control flow without especially burdensome implementations or any runtime performance cost (ignoring cache effects and whatnot), and the whole thing is statically guaranteed to work (within the bounds of what Zig actually promises).
- LAC-Tech 4y ago- dealing with C level abstractions in a very ergonomic way - ability to import and use C libraries, no FFI or custom bindings required - clear & logical distinctions between different pointer types and arrays - allocators as a first class concept. this makes zig really safe because you can pass in test allocators that will report leaks etc. If you enjoy hot takes and ancient memes, I made a talk about zig a couple of months ago where I lay out why I think it's The Chosen One to succeed C. https://www.youtube.com/watch?v=kCmOfRcmkN0 https://www.youtube.com/watch?v=kCmOfRcmkN0
- belmarca 4y agoProgramming language experts told Andrew Kelley, the creator of the Zig programming language, that having code which could run at compile time was a really dumb idea. But he went ahead and implemented it anyway. Years later, this has proven to be one of the killer features of Zig. In the Zig world, we call it comptime, from the keyword used to mark code required to run at compile time or variables to be known at compile time. Which experts? The "comptime" is just macro-expansion from Scheme/Lisp which has been around for a long time. Aren't C++ templates also "code that runs at compile time"?
- wisnoskij 4y agoJai also leans very heavily into this I believe.
- schaefer 4y agoCan we get Jai compilers yet? Or is it still for in-house use only?
- eddsh 4y agoWhat's Jai?
- P5fRxh5kUvp2th 4y agoIt's a language written by Jonathan Blow, meant to replace C++ for game development. reportedly his game "The Witness" was written in it. He has a series of video's where he explores what the language will look like as he's developing it, and has claimed he'll open source it once he feels it's ready. He wants to maintain control until he feels it's no longer half-baked. IIRC, he demonstrates things like a music player running during compile time. It's a core tenet of his language.
- zem 4y agoplaying the jeopardy tune during long compilations would be awesomely hilarious (:
- billfruit 4y agoZig tutorials and learning materials and on-boarding experience needs a lot of work. Compare with something like Kotlin, so well designed is its introductory material that one could start doing useful things with Kotlin within a few hours. Also I find Zig's choice to use abbreviated keywords rather cryptic, for example using 'fn' instead of 'function' only hurts readability I think.
- schaefer 4y agoThe ziglings tutorial is pretty good! And if you go digging, I think you can still find solutions for last year’s advent of code.
- 3a2d29 4y agoZig isn't 1.0 so I assume more learning materials will come along once the language is actually finished. I am not super familiar with zig, but I would say I am not a fan of using the entire word "function" I believe "fn" is used in rust and I think its readable. Maybe something like Go's "func" is better, but I really feel this is all arbitrary.
- billfruit 4y agoTo my eye 'public function main' is a lot better than 'pub fn main'. It is also lot more clearer when you have to read out code phrases out loud.
- 3a2d29 4y agoIs there any language other than javascript that has the key word as the full "function"? Again its personal preference, but I'm curious if there is a language you use often that makes you like that specifically? Java is a bit like that, but has the C++ data types as its function key word basically. I'm trying to think if I am forgetting one.
- int_19h 4y agoSome examples would be Ada, Lua, Pascal (and derivatives like Delphi), XQuery.
- ReactiveJelly 4y agoIt's the only way to cross-compile C and C++ code without losing hair follicles
- gavinray 4y ago"In which people who have never used a language with compile-time expressions try Zig and think it's novel" Not knocking Zig, I think it's swell, but it wasn't near the first language with this feature. D comes to mind, and C++ has it now with "constexpr" and "consteval".
- anonymoushn 4y agoI've ported a small Zig program to C++ and spent a long time and a lot of lines of code on the stuff that comptime can do which C++ cannot.
- gavinray 4y agoI'd be curious for examples. In a weird spot where I'm decently familiar with C++, D, and Zig and it would make for good blogpost material to compare comptime implementations for the same thing across languages IMO.
- WalterBright 4y agoI've posted some examples here for D.
- socialdemocrat 4y agoThe purpose was to pick interesting features to talk about and select one language for each feature which is a good exponent for that features. For instance I used Smalltalk to cover image based development: https://erikexplores.substack.com/p/what-makes-smalltalk-unique https://erikexplores.substack.com/p/what-makes-smalltalk-uni... Yet Smalltalk was not the first language supporting image based development. I would pick Julia to talk about multiple dispatch. Likewise Zig is picked not because it is the first doing comptime but because that is a central feature of Zig and it is a language which really puts emphasis on it.
- password4321 4y agoThe whole "Zig doesn't have warnings" thing didn't go over well last month. https://news.ycombinator.com/item?id=32752383#32752885 https://news.ycombinator.com/item?id=32752383#32752885 I bought into the focus on the build toolchain, until I found out my old Mac wasn't supported.
- malkia 4y agoSo how are loops in compile time avoided? Is there a timeout in the compiler, or N number of step/instructions before bailing out?
- deleted 4y ago[deleted]
- mjan22640 4y agoThe "Code run at compile time" feature has been around since ages. Haskell templates are in haskell, the "compile time and run time language are the same" feature is not unique either.
- ctrlmeta 4y ago> Programming language experts told Andrew Kelley, the creator of the Zig programming language, that having code which could run at compile time was a really dumb idea. Really? I can't believe that! Running code at compile time is as old as Lisp! And it is present in some form or other in some other popular programming languages too. Like constexpr and templates in C++.
- hu3 4y agoBoth can be true. > experts told Andrew Kelley, the creator of the Zig programming language, that having code which could run at compile time was a really dumb idea > Running code at compile time is as old as Lisp Old ideas are not immune to be considered dumb by some people.
- junon 4y agoTerra has had this for a long time. Anything you wrote in Terra could be invoked at compile time just the same using LLVM's JIT stuff. Made writing game tooling a breeze.
- diceduckmonk 4y agoTerra is such a an overly recycled term. It was not obvious if this was some kind programming language associated with the Terra block chain and Terraform Labs, which is also overloaded with Terraform, the infrastructure-as-code tool by Hashicorp.
- junon 4y agoI'm not sure what your point is. Terra is a programming language. In the context of programming languages, I would assume finding the correct Terra would be trivial.
- socialdemocrat 4y agoI considered writing about Terra as ab examples of a similar language but I felt the fact that Terra did this at JIT time made it too different to compare. In that regard it is not all that different from Julia. That Zig does this AOT is significant I think.
- junon 4y agoI'm not sure what you mean. Terra does JIT to do AOT compilation. You run either Lua _or_ native code (JITed) at compilation time to produce Terra code that ultimately gets emitted into the final executable.
- socialdemocrat 4y agoSo you can create a distributable executable without any of the Lua code? Or will the exe contain a Lua interpreter?
- coldtea 4y agoIt's lacklustre unicode string support. Pretty unique for a modern language!
- defen 4y agoWhat kind of things would you like to see? And do you think they should be built into the language? Standard library? Blessed third-party package?
- p0nce 4y agoSaying your product were first to do X, when by all measures it wasn't first to do X, is the oldest play in the book of marketing.
- keeeeeeeem 4y agoI can't speak to the quality of Zig's compile-time compilation, other than to comment and say the article is taking liberties with its use of the word unique. As other commenter's have noted, many languages have the feature. Instead I'm going to talk about a bunch of cool and weird compile-time code implemented by people much smarter than I. If anyone reading this is interested in compile-time computation, you owe it to yourself to read Paul Graham (of HN)'s own work on compile time programming, "On Lisp"[0], or to take compile-time ideas to the next level, Doug Hoyte's "Let Over Lambda"[1] (LOL). Lisp languages have a long and interesting history of compile-time computation via their various macro systems, and given the first-class inclusion in most Lisps, a greater variety of ideas have been explored regarding compile-time computation. A few interesting examples: LOL's "Pandoric macro"[2] lets you monkey-patch closure values. It's absolutely bonkers and would almost certainly be pathological to introduce to a codebase, but it's an example of pushing the boundaries of what's possible. Common Lisp's object system, implemented via macro[3]. To be honest, the entire Common Lisp language is a masterclass when it comes to learning about macros (I can't avoid mentioning Scheme's hygeinic macro system and Lisp-1/Lisp-2.) A special non-Lisp shout-out goes to Rust's Diesel library for embedding SQL DDL queries into macros[4] which is not something I've personally seen before. Clojure has a few interesting (and practical) macro libs, particularly core.async, which is an implementation of CSP (similar to Golang's channels AFAIK), it embeds perfectly into the existing language and extends its capabilities even though it's merely a library. Another interesting lib which comes to mind is Meander[5], which uses term-rewriting to provide transparent data transformations. Think declaratively interacting with data structures at compile time, and the library figuring out the best way of turning it into imperative value manipulation code. [0] http://www.paulgraham.com/onlisp.html http://www.paulgraham.com/onlisp.html [1] https://letoverlambda.com/ https://letoverlambda.com/ [2] https://letoverlambda.com/index.cl/guest/chap6.html#sec_7 https://letoverlambda.com/index.cl/guest/chap6.html#sec_7 [3] https://mitpress.mit.edu/9780262610742/the-art-of-the-metaobject-protocol/ https://mitpress.mit.edu/9780262610742/the-art-of-the-metaob... [4] https://diesel.rs/ https://diesel.rs/ [5] https://github.com/noprompt/meander https://github.com/noprompt/meander
- kazinator 4y agoSpeaking of Lisp, macros and compile-time evaluation, it's amazing that there isn't a standard macro macro-time which evaluates its argument and replaces it with the quoted object resulting from its evaluation. It's so easy to write! Blatant, arbitrary compile-time evaluation (other than code transformation) is not that common in Lisp programs; which could be why such a macro wouldn't be used a lot. For complicated, calculated constants, the Common Lisp defconstant has certain semantics that work in that space, where responsibilities are foisted onto the programmer: [A]n implementation may choose to evaluate the value-form [of defconstant] at compile time, load time, or both. Therefore, users must ensure that the initial-value can be evaluated at compile time (regardless of whether or not references to name appear in the file) and that it always evaluates to the same value. In Common Lisp, arguably, defconstant is the mechanism of choice for blatant compile-time evaluation for actually obtaining a value from a complex expression which is thereafter treated as a constant. And its semantics isn't precise enough for the task of retrieving something from a specific environment. Using defconstant to access a file in the compilation environment won't work if the evaluation is done at load time. In any case, defconstant looks very similar to these constant expression mechanisms that are cropping up.
- dolmen 4y agoHere is at least one thing that Andrew said: > There are a lot of people in this thread saying that I said stuff I didn't say. It's maddening. Source: https://news.ycombinator.com/item?id=33107359 https://news.ycombinator.com/item?id=33107359
- JohnDeHope 4y agoI'd be perfectly happy if the answer to TFA's question was "nothing". Zig has the minimalism of something like C89 or Lua. It has a syntax like C. It has compile time execution (as per TFA) like all sorts of languages mentioned in the comments. Etc. There isn't really anything particularly unique about it. Who cares? It feels to me like zig is an example of something that is greater than the sum of its parts. Really the only thing I don't think I've ever seen before is how zig passes around memory allocators. And that's probably because I'm not a systems programmer by trade, so I'm less familiar with that sort of thing.
- b3morales 4y ago> Zig will compile different variants of maximum for each case, where maximum is called with a different set of argument types How does Zig handle the monomorphization problem for generics in library APIs? In other words, give the `maximum` function from the blog post, if I want to distribute a binary but make that function available to clients of my binary to call with different types, what does Zig do?
- scraptor 4y agoAs far as I am aware, you can't do that.
- ecshafer 4y agoI think Zig is pretty cool. I have played with it a bit. My main complaint I think is that the toolchain is pretty hard to get working right in windows, though its a breeze on linux. The only reason I would care about windows, is that I think it looks like a great language for game development since its fast, pretty easy to write, and interops with C/C++ pretty easily.
- efnx 4y agoA sufficiently expressive (and safe) type system is Turing complete, and many languages have that. They don’t all feel ergonomic, but Haskell and Rust are pretty good. If you’re into it, you should check out Idris (lang) and Idris2. Both very cool languages that support dependent types (types that depend on values).