Hacker News new | past | comments | ask | show | jobs | submit login

I think that Zig's simplicity hides how revolutionary it is, both in design and in potential. It reminded me of my impression of Scheme when I first learned it over twenty years ago. You can learn the language in a day, but it takes a while to realize how exceptionally powerful it is. But it's not just its radical design that's interesting from an academic perspective; I also think that its practical goals align with mine. My primary programming language these days is C++, and Zig is the first low-level language that attempts to address all of the three main problems I see with it: language complexity, compilation speed, and safety.

In particular, it has two truly remarkable features that no other well-known low-level language -- C, C++, Ada, and Rust -- have or can ever have: lack of macros and lack of generics (and the associated concepts/typeclasses) [1]. These are very important features because they have a big impact on language complexity. Despite these features, Zig can do virtually everything those languages do with macros [2] and/or generics (including concepts/typeclasses), and with the same level of compile-time type safety and performance: their uses become natural applications of Zig's "superfeature" -- comptime.

Other languages -- like Nim, D, C++ and Rust also have a feature similar to Zig's comptime or are gradually getting there -- but what Zig noticed was that this simple feature makes several other complex and/or potentially harmful features redundant. Antoine de Saint-Exupery said that "perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away." I think that Zig, like Scheme -- and yes, there are others -- is close to that minimalist vision of perfection.

What a truly inspiring language. Rather than asking how we could make C++'s general philosophy work better as another increasingly famous language does, IMO, it asks how we could reshape low-level programming in a way that's a more radical break with the past. I think it's a better question to ask. Now all there's left to hope for is that Zig gets to 1.0 and gains some traction. I, for one, would love to find a suitable alternative to C++, and I believe Zig is the first language that could achieve that in a way that suits my particular taste.

[1]: I guess C has the second feature, but it loses both expressivity and performance because of it.

[2]: Without the less desirable things people can do with macros.




I've been a financial backer of Zig for several months now, and plan to continue, because Andrew and the other contributers are pushing language design in directions that no other language is.

That being said, Zig's comptime is not a proper replacement for typeclasses/traits. Zig can do both comptime duck typing and vtable-based dispatch, but it cannot do proper bounded polymorphism type checking. It always fully evaluates types before type checking them. This might make it difficult (or impossible?) to provide type checking error messages of similar quality to Rust. I'm not sure if there are any other practical consequences for realistic programs, though. I suspect there might be issues around interface stability gaurantees, though I can't quite put my finger on why.


Right, they are only approximately comparable, but the important thing to remember is that features of formalisms are never goals in themselves, but rather means to various ends. The goal is never "to have interfaces/typeclasses/traits" but to be able to specify an algorithm that works for a variety of data structures with shared properties. Moreover, I think it would be a mistake for Zig library authors to try and replicate styles used in other languages. Zig provides sufficient mechanisms for expressing programs, and it will develop its own style. There will be elements that are analogous to those in other lanugages, but not identical.

Having said that, I do support a proposal for specifying a type at the parameter declaration with some type -> bool function.

I like this quote by Leslie Lamport about comparing formalisms (he talks about specification languages rather than programming languages, but the sentiment is the same):

> Comparisons between radically different formalisms tend to cause a great deal of confusion. Proponents of formalism A often claim that formalism B is inadequate because concepts that are fundamental to specifications written with A cannot be expressed with B. Such arguments are misleading. The purpose of a formalism is not to express specifications written in another formalism, but to specify some aspects of some class of computer systems. Specifications of the same system written with two different formalisms are likely to be formally incomparable… Arguments that compare formalisms directly, without considering how those formalisms are used to specify actual systems, are useless.


> Other languages -- like Nim, D, C++ and Rust also have a feature similar to Zig's comptime or are gradually getting there -- but what Zig noticed was that this simple feature makes several other complex and/or potentially harmful features redundant.

I'm curious where this impression of Zig comes from, as this is precisely what Nim has set out to do: a small core extensible via metaprogramming. Are there features that Nim implements which go against this premise? if so, what are they? :)


> Are there features that Nim implements which go against this premise? if so, what are they? :)

Nim has generics (plus concepts), templates, and macros. Zig has just comptime, through which it achieves all those goals (minus some macro capabilities that it deems harmful anyway) with just one, very simple, cohesive construct. You could argue on whether you like this or not, but you can't argue that Zig's approach isn't fundamentally more minimal. Zig is a language you can reasonably fully learn in one day; I don't think you could say the same about Nim.

---

Also, note another remarkable feature. One could define a language called Zig' with the following properties:

1. Every well-formed Zig program is a well-formed Zig' program (i.e. Zig' accepts all Zig programs, potentially more).

2. Every Zig program has the same semantics as the identical program when interpreted in Zig'.

This means that to analyse the semantics of a Zig program you can pretend it's a Zig' program; in fact, you don't need to create an interpreter for Zig', you can just pretend it exists. Why would you want to do that? Because Zig' is simpler. How? Here's the kicker: Zig' ignores comptime completely. It is a very simple, optionally-typed, dynamic language with reflection.

In other words, to analyse the semantics of a Zig program you can forget about comptime and pretend it can do everything at runtime (and treat comptime as a pure semantics-preserving optimisation).

This is not true for languages with macros, as they are not "erasable".


> Zig has just comptime, through which it achieves all those goals (minus some macro capabilities that it deems harmful anyway) with just one, very simple, cohesive construct. You could argue on whether you like this or not, but you can't argue that Zig's approach isn't fundamentally more minimal. Zig is a language you can reasonably fully learn in one day; I don't think you could say the same about Nim.

"some macros" are downplaying Nim macros, it's like saying Lisp has some AST rewrite capabilities.

Nim macros goals are two-folds:

1. adding functionality to the language without baking it in the compiler. A prime example is ``async``, ``async`` including a nice async/await syntax can be completely implemented as a library without reserving keywords to do things like `pub async fn`.

2. Automating away boilerplate.

From what I understood, Zig comptime is only about making compile-time function evaluation first-class.


Yes, plus introspection. Zig tries very, very hard to avoid macros, so macros are an anti-feature from Zig's perspective. That you could do what Zig finds important for its domain, like conditional compilation, writing a typesafe println, generic types, and generating pretty-printing routines all in simple Zig without macros is a cool discovery. I don't know if it's true, but I think the desire to avoid macros at all cost was a bigger motivation for Zig's design than, say, generic types.

In other words, there is a capability here that Nim really, really wants, and that Zig really, really doesn't want, so on that front they are not competing in their designs.


I do love how zig's comptime naturally led to generics without extra syntax. But after using nim i'm convinced that macros make even more sense for systems programming. They can even affect performance -- nim's macros can generate types that would be difficult to write by hand.

I also take issue with your statement that zig is "more minimal" since that only applies to the user's perspective -- from the compiler's perspective, macros make a language far more minimal. But i vaguely recall already discussing this distinction with you so i don't want to rehash it.

At any rate i will definitely be paying attention to andrew's progress, he has a really clear vision.


> But after using nim i'm convinced that macros make even more sense for systems programming.

Macros are controversial. I love them in Scheme and Clojure, but I wouldn't want them in any language aimed at a larger, more mainstream crowd. At the very least, macros introduce another meta-language to know (and if they're in a language with a complex type-level language like Rust or Haskell then they're a third language within the language), but I think it's a matter of personal aesthetic preference.

> But i vaguely recall already discussing this distinction with you so i don't want to rehash it.

Maybe we did. :)


Look at any sufficiently complex and/or low level C like Linux or FreeRTOS, and C text macros are used significantly. Some of the functionality would be horrible to implement otherwise. Being text based they're a pain, but like @gw, I'd have a hard time seeing a language like Zig without macros making a good low level system language. Maybe a good systems application like Kubernetes, similar to Go's niche, but not system kernels.


The goal isn't to have macros but to be able to do what macros are used for. Zig has found a different and simpler way to do what macros do. comptime gives you generic types, typeclasses/concepts, typesafe printf, conditional compilation and much more, all without macros and with a simpler construct than macros.


After reading your previous comment, I read a bit more about comptime. It does seem to be able to handle most of the cases I could think of wanting macros for, and it is a nice UX in that it's really like "bounded macros" and prevents building arbitrary new semantics. Though thinking on two levels in a given function does seem tricky to me, but perhaps that's just familiarity. Personally, I kind of like having separate macro vs code. Comptime seems like it'd almost be a better fit for Go than their generics proposal.


The beauty of comptime is that, unlike with macros, you don't need to think on two levels. The semantics is the same as if everything were done at runtime. To read a comptime function you can completely ignore the distinction between compilation time and runtime. To write it you need to know that some operations are only available at compile-time.

See my comment here about "Zig' ": https://news.ycombinator.com/item?id=24293611

Perhaps now you see what I meant when I said that Zig's simplicity hides its radical design.


In Nim the equivalent is using a `static:` block, then everything inside has normal Nim syntax but evaluated at compile-time.

You can also do `const a = static(foo(x, y, z))` to force normal function to be evaluated at compile-time and store them in a constants.

Hence you don't need to use macros for compile-time evaluation in Nim just like in Zig. However macros are necessary for AST manipulation.


> What a truly inspiring language

It's indeed an inspiring language, and rust is taking inspiration from it already: https://github.com/jswrenn/project-safe-transmute/blob/rfc/r...

> lack of generics

I can't wait before Zig2 comes and eventually adds generics…


> and rust is taking inspiration from it already

But C++/Rust can never have Zig's primary feature -- simplicity. Zig's power is not that it has comptime, but that it has little else.

> I can't wait before Zig2 comes and eventually adds generics…

No need. Zig gives you the same capabilities as generics do, only through a separate feature that other languages also have in addition to generics. In other words, it has generics, but without having generics as a special construct. Zig recognises that once you have that other feature (compile-time introspection) you don't need generics as a separate construct, but they can be just an instance of that single construct.


> But C++/Rust can never have Zig's primary feature -- simplicity

Sounds like a Go pitch, except Zig ain't Go. And while comptime is a cool feature, it's also a really complex one!

> In other words, it has generics, but without having generics as a special construct. Zig recognises that once you have that other feature (compile-time introspection) you don't need generics as a separate construct, but they can be just an instance of that single construct.

This has advantages (only one feature to know), but it also has a big drawback: the lack of orthogonality. C is also simple, for instance it has no concept of errors (only return values) or arrays (only pointers), but most people won't consider this a good idea (and Zig didn't follow C on either of those two design points)

Zig is cool, but I hoped the “generics are too complex of a feature” meme would die now that Go is getting generics, and I'd be really sad to see come back…


> it's also a really complex one!

No, it's a very simple one, so much so that it's erasable: https://news.ycombinator.com/item?id=24293611 And still it is probably the most complex aspect of Zig.

> Zig is cool, but I hoped the “generics are too complex of a feature” meme would die now that Go is getting generics, and I'd be really sad to see come back…

You've misunderstood me. Generics are a good thing -- if that's all you have. But if you have generics and procedural macros, it turns out that you can do the work of both with a feature that's simpler than either. The capability generics add is a very important one, but given that low-level languages need another one as well, it turns out that generics can be subsumed into that one without being a separate and additional construct. Zig has generic types and concepts/typeclasses; these just aren't atomic language constructs.


> But if you have generics and procedural macros, it turns out that you can do the work of both with a feature that's simpler than either.

Here I think we just have a different subjective perception of what simplicity is. I much prefer have two orthogonal systems which do their own business than having a single more powerful tool than do both (like having slices + references instead of the all powerful pointer)

Anecdotal note: more than 10 years ago, the Go team pitched why they didn't need generics nor macros, because code generation would solve both problems (+ others), and now they're on their way back to add generics to Go (with a lot of hassle).


> I much prefer have two orthogonal systems which do their own business than having a single more powerful tool than do both (like having slices + references instead of the all powerful pointer)

OK, but that's not quite the situation. Here we're talking about languages that have, or will have, the single "more powerful" construct, and also the more specific, special case one, as two separate constructs, even though one of them would have sufficed.

Again, Zig has parameterized types, and very elegant and powerful ones -- they're functions that take some types as argument and return a type. It just doesn't have generics as a separate construct. Rather, it is a special case of a more general one (that Rust and C++ will also have).


Zig comptime functions that take and return structs consisting partially (or wholly!) of types reminds me of OCaml functors, and should perhaps be compared to them rather than generics.


"C is also simple, for instance it has no concept of [...] arrays (only pointers)"

That is a confusing part with C. C do have a concept of arrays, but not as function arguments. You notice it first with multidimensional arrays.


Zig already has generics, gp was imprecise. They are not a part of the language, but an emergent feature that is simple enough to implement using comptime.

Edit: clearly gp was not mistaken, just imprecise.


In Zig types are values (same as any other value.) This lets you do generics without the need for any special syntax; you can simply pass types as parameters and return types.


Looks like comptime is what they already have instead:

https://ziglang.org/documentation/master/#Introducing-the-Co...


Clearly you meant zig++


(This was a reference to the Go language, which after a decade saying “generics aren't needed” and even “the lack of generics is a feature”, are eventually shoehorning them in the language in their Go2 campaign.)


I think they've been saying "We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it." since 2013: https://web.archive.org/web/20130410000959/https://golang.or...


That's what they've been saying, but I don't buy it.

I mean, the workarounds are horrible code generation tools and reflection. How were those ever not considered to be more complex than generics?


Do you have any example code? It's plain to see that Zig's comptime is powerful enough for typeclasses, but it's not at all obvious that it'd be as ergonomic as Haskell's typeclasses.


I don't have any particular examples in hand, but the question you're asking is one that's tough to answer because the languages that might be as ergonomic as Haskell in that regard and are also low-level are significantly more complex than either Haskell or Zig, and so I don't think we have a good point of comparison (I think Zig is revolutionary). There is definitely a price to pay for being a high-control/low-level language, and it certainly requires that you spend some of your "complexity budget" on things that high-level languages like Haskell or Java don't have to. But I think Zig shows that you can be both low-level and reasonably "expressive" without also being so much more complex than most high-level languages.


> ...lack of macros and lack of generics ... To be fair, you can do absolutely everything you want to do in C++ without using either of these features at all, it just takes a bit of discipline.

And if you see the flack that go gets for not including generics then I'm not so sure that that is a great way to get people to adopt your language.


Zig has generics, they are just not part of the language (very easy to implement using what zig gives you).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: