←back to thread

Zig is hard but worth it

(ratfactor.com)
401 points signa11 | 4 comments | | HN request time: 0.001s | source
Show context
pron ◴[] No.36150237[source]
> there’s not a direct correlation between the slimness of a language’s syntax and ease of learning

That's absolutely true, but (the standard library aside) the "syntax" -- or, rather the syntax and core semantics -- of a programming language are arbitrary axiomatic rules, while everything else is derivable from those axioms. So while it is true that a small language can lead to a not-necessarily-easy-to-learn overall programming experience, it is the only arbitrary part, and so the only part you need to memorise (or consult the documentation for when dealing with some subtlety you may have forgotten). So a smaller language reduces the need for "language lawyering" after you learn it.

Some languages (e.g. lisps) are deceptively small by relying on macros that form a "second-order" language that interacts with the "first-order" language, but Zig doesn't have that. It has only one language level, which is small and easy to memorise.

But yes, Zig is a bigger language than C, but a far smaller language than C++. What's amazing, though, is that C++ is strictly more expressive than C (i.e. there are programs that could grow exponentially faster in C than in C++, at least without the help of C macros), but Zig is as expressive as C++ (i.e. program sizes may differ by no more than a small constant factor) while being much closer to C in size, and it achieves that without the use of macros.

replies(4): >>36150398 #>>36150954 #>>36151058 #>>36153690 #
the_duke ◴[] No.36150398[source]
I'm not a Zig expert, but I have a different take here.

Zig has comptime, which is essentially a compile time macro written in the main language and with type reflection capabilities.

They can introduce complex behaviour and fail in very cryptic and unexpected ways, which results in an experience very similar to macros or C++ template literals.

replies(1): >>36150717 #
pron ◴[] No.36150717[source]
The objects that are manipulatable by comptime are ordinary program objects and types -- not ASTs. That means that while it's true you can get compile-time errors in similar situations to macros, the errors themselves are like ordinary runtime errors in an untyped language -- while occurring at compile-time, they look like runtime error in Python or JS -- rather than errors due to some "second-order" manipulation of symbols like templates or macros, and so are easier to diagnose (you get a regular stack trace for one).

There is also another interesting difference, albeit a theoretical one. Zig's comptime is what's known in formal languages to be referentially transparent (it basically means that you cannot distinguish between two otherwise identical objects that differ only in their reference name) while macros are not. Because referential transparency is strictly less expressive than "opacity" (but it's also simpler!), it's surprising that so many practical use cases for macros can be addressed with the less powerful (but simpler and much easier to debug) comptime. That's quite a discovery in language design. While other languages also have comptime-like constructs, they also have other complex features that have made it hard to see just how powerful something like comptime alone can be.

replies(3): >>36151303 #>>36151943 #>>36156263 #
auggierose ◴[] No.36151303[source]
It's not really surprising that purely functional programming is expressive. Indeed, it is as expressive as "opaque" programming.
replies(1): >>36151398 #
pron ◴[] No.36151398[source]
There's nothing pure functional here (perhaps the term "referential transparency", which some FP fans have come to misunderstand and perpetuate its misunderstanding is what may have given you that impression). Referential transparency is very much less expressive than referential opacity, as there are certain statements that simply cannot be expressed if your language is referentially transparent. For example, in programming, a referentially opaque expression can refer to the name of the variable holding some value. In programming, languages like Zig and Java are more referentially transparent than languages like C and Haskell because the latter have macros.
replies(2): >>36151778 #>>36151826 #
whateveracct ◴[] No.36151778[source]
Can you give me an example of a Haskell expression which isn't reverentially transparent (without unsafePerformIO)?

> An expression is called referentially transparent if it can be replaced with its corresponding value (and vice-versa) without changing the program's behavior.

^ that is the definition of referential transparency I am aware of.

You seem to be implying that FPers have bastardized the term through their misunderstanding.

But the bog standard FP definition is a real and useful concept. Maybe it stole something else's name? But I don't think it's due to being mistaken. Because the FP concept itself is pretty rigorous.

replies(1): >>36153339 #
1. pron ◴[] No.36153339[source]
> Can you give me an example of a Haskell expression which isn't reverentially transparent (without unsafePerformIO)?

Yes: https://github.com/ncaq/debug-trace-var The trick, however, is not unsafePerformIO (destructive mutability has nothing to do with referential transparency in general, although it breaks it in Haskell specifically) but with TemplateHaskell, as quoting has everything to do with referential transparency.

> But the bog standard FP definition is a real and useful concept.

Actually, it's rather tautological. It defines FP circularly (see my comment here: https://news.ycombinator.com/item?id=36152488). It says nothing more than the far more useful explanation: "the meaning of every expression is a value".

replies(1): >>36153671 #
2. whateveracct ◴[] No.36153671[source]
hm okay so basically anything that doesn't use TH or unsafePerformIO is gonna be referentially transparent. And TH is even deferentially transparent at TH-time. It only "breaks it" when evaluating the whole program. But each "stage" maintains the property.

I'm assuming any pure language with macros is also r.t. at each stage and only pedantically breaks r.t. when combined. But I don't think that especially hurts the ability to do fast and loose reasoning so long as the core language is pure.

It definitely doesn't seem correct to say Java is more referentially transparent than Haskell here. You don't have to go into such niches in Java to lose that property.

replies(1): >>36154194 #
3. pron ◴[] No.36154194[source]
> You don't have to go into such niches in Java to lose that property.

It's not so easy. You'd have to examine debugging information in stack traces and use reflection. You can't write such a "trace" operator in Java or in Zig. Of course, without macros, C is almost perfectly referentially transparent and Haskell is, too (except for unsafePerformIO).

replies(1): >>36156947 #
4. whateveracct ◴[] No.36156947{3}[source]
You can also mutate a list that is passed in and suddenly you cannot do substitution to reason about your program.