Live data from Hacker News

Zig: software should be perfect [video]

youtube.com

121–130 of 141 posts

Re: Zig: software should be perfect [video]

#121

Earlier quoted context omitted.

> the compiler will statically guarantee the impossibility of null dereference exceptions, almost every language that gets rid of nulls with something like the Option type will let you still bypass it and get a null reference exception. Rust lets you unwrap, F# lets you bypass it. You could at least enforce a lint that doesn't allow the bypasses in projects where that is desired though.

Perfect is the enemy of good. By reducing the possibility of null dereference exceptions from 100% to 10% you have reduced the cognitive burden by 90%. Removing the bypass would result in a 100% reduction in cognitive burden, only 10% more than the second best solution. However handling null cases correctly isn't free either. Especially when you know that a value cannot be "null" under certain conditions which those…

> However handling null cases correctly isn't free either. Especially when you know that a value cannot be "null" under certain conditions which those 10% fall under.

While I agree there are rare cases where .unwrap() is the right thing to do, I actually disagree here that it’s anywhere close to 10%: If you want to write a function that accepts only non-null values in Rust, you simply write it as such! In fact, this is the default, and no cognitive burden is necessary: non-nullable T is written simply as “T”. If you have an Option and want to convert it into a T in Rust, you simply use “if let” or “match” control flow statements.

I actually think using .unwrap() in Rust anywhere but in test code or top-level error handling is almost always a mistake, with perhaps 0.001% of exceptions to this rule. I write code that never uses it, except those cases mentioned; while I’ve run into situations where I felt at first .unwrap() was appropriate, I took a step back to think of the bigger picture and so far always find safer solutions to yield a better overall design.

The cognitive burden from Rust comes not from this, but almost entirely from the borrow checker (a completely different toptic), and in some cases, arguably inferior “ergonomics” vs how Zig or Kotlin handle optionals.

For example, in some null-safe languages, you can write:

  if (myObject) { myObject.mehod(); }
And the compiler will understand this is safe. Whereas, in Rust, you must write:

  if let Some(x) = myObject { x.method(); }
This is not even to mention that Rust has no built-in shorthand for Option (some languages write “T?” for example), but I understand why they chose not to build this into the language; rather, Option in Rust is actually a component of the stranded library! In a way, that’s actually quite cool and certainly is by-design; however, it doesn’t change the fact that it’s slightly more verbose.

IMO it’s not a huge deal, but certainly Rust could benefit from some syntax sugar here at least. Either way, both examples here are safe and statically checked by the compiler.

Re: Zig: software should be perfect [video]

#122

Earlier quoted context omitted.

I made this argument in the talk: the only problem with exception-based handling is the lack of explicitness. It's too easy to call functions without being aware of the set of possible errors. Many c++ projects disable exceptions entirely. Writing "exception safe" code is tricky and non-obvious. Functions which should be guaranteed to never fail often can throw std::bad_alloc. Try-catch syntax forces incorrect nestin…

> It's too easy to call functions without being aware of the set of possible errors. But that's entirely fine! Programmers are far too obsessed with exactly which functions trigger which errors when it absolutely doesn't matter. All you need to know is what errors you can handle and where in the code you can handle them . If there is a network exception and can recover and retry it at the start of the operation, it l…

This [1] article by Raymond Chen is one of my favorite on exceptions.

I admit there is an elegance to exceptions, but when I'm trying to write reliable code, I find reasoning about exceptions significantly increases my cognitive load. Error handling is a place where a little more verbosity is okay because it keeps my focus on the local context rather than having to consider the entire call stack. I basically agree with the TL;DR of the article: "My point is that exceptions are too hard and I'm not smart enough to handle them".

[1] https://blogs.msdn.microsoft.com/oldnewthing/20050114-00/?p=...

Re: Zig: software should be perfect [video]

#123

There's a certain irony that the presenter quickly dismisses exception-based error handling, and then the first example handles an error by printing a message and exiting -- exactly what an unhandled exception does. This is more than a small piece of irony with a somewhat artificial example. Often, there simply isn't very much you can do with an error. In a SaaS world, you might not be able to tell the user what wron…

> the only problem with exception-based handling is the lack of explicitness. [...]. Writing "exception safe" code is tricky and non-obvious.

Does it still hold if you assume automatic resource (memory/locks/handles/etc.) freeing? (e.g RAII)

Re: Zig: software should be perfect [video]

#124

Earlier quoted context omitted.

> It's too easy to call functions without being aware of the set of possible errors. But that's entirely fine! Programmers are far too obsessed with exactly which functions trigger which errors when it absolutely doesn't matter. All you need to know is what errors you can handle and where in the code you can handle them . If there is a network exception and can recover and retry it at the start of the operation, it l…

This [1] article by Raymond Chen is one of my favorite on exceptions. I admit there is an elegance to exceptions, but when I'm trying to write reliable code, I find reasoning about exceptions significantly increases my cognitive load. Error handling is a place where a little more verbosity is okay because it keeps my focus on the local context rather than having to consider the entire call stack. I basically agree wi…

This kind of thinking has permeated my coding practice.

I used to think "this is hard, I need to learn about more systems until I'm smart enough to do this."

Now I think "this is hard, there's probably something wrong with the architecture. I'll fix it."

Re: Zig: software should be perfect [video]

#125

Earlier quoted context omitted.

In Zig the default target is native, which turns on all the applicable CPU features. If you want to target something other than the native machine you can use --target-os --target-arch parameters. I haven't exposed extra CPU options for cross compiling yet.

Do you consider the default architecture targetted by GCC (e.g. some old Intel) to be cross compiling? That is, can I make a binary that supports most x86 processors rather than just those with the particular extensions I support, with the current Zig compiler?

Yes. You can pass the native OS and native arch as the cross compilation target, and produce a binary that runs on your machine and others that don't have the same extra CPU features.

Re: Zig: software should be perfect [video]

#126
post #59

Earlier quoted context omitted.

While I agree with the content of your post literally, I think we often underestimate the importance of software reliability and performance, and end up giving it less attention than it deserves. I understand the place for rapid prototyping etc., and that not every software application deals with life-and-death situations — but even those that aren’t, I think our industry suffers a bit here. For example, to this day…

In over 6 years working in this industry, in projects with anywhere from dozens of users to millions of users, not a single bug I have encountered was caused by a "hidden allocation" causing the process to go OOM. Not one instance. One of the two examples he cited in his introduction, the Android one, wasn't even caused by an unhandled OOM error. Android by design will kill unused processes if they're occupying memor…

Yes for most userland applications removing hidden allocations is not a concern, which is why for most general purpose programming languages this is way down the priority list.

Most languages make allocations behind your back so that your code can focus on the logic you actually care about since you could not do anything about running ot of memory anyways.

However there are projects where that level of control matters. For those projects C is currently still the default choice, even though it was designed more than 40 years ago. Some choices made back then might be huge liabilities for code we are writing now, because we still need that kind of language. A modern alternative to C could provide a huge value to all of us, mostly through more correct, secure and/or performant software.

Re: Zig: software should be perfect [video]

#127
post #98

Earlier quoted context omitted.

What you mean is that C++ doesn't have a way to (easily) let you check whether a given reference is null or not. int* a = NULL; int& b = *a; compiles and runs just fine.

No the gp is correct, references in c++ can't be null. Your code invoked undefined behavior before you did anything with a reference, namely *a which is a null pointer dereference.

The "null problem" is that a static language does a run-time check instead of a compile-time check. By the time the undefined behavior is invoked, compilation ended.

Re: Zig: software should be perfect [video]

#128

Earlier quoted context omitted.

I think it would. I posted some observations on this the last time this video was discussed: https://news.ycombinator.com/item?id=17187140 My hypothesis is that Zig's compiler passes the equivalent of -march=native to the backend, which is why it should also be given to the C compiler to give a fair comparison (and a speedup of 30% or so).

I think you are probably right. I will do a follow-up post addressing the performance claims in this talk. I think I owe it to the community.

Much appreciated. Given Zig's design and use of LLVM, for code translated "line by line" (same algorithm) I don't see any reason for Zig's performance not to be substantially identical to C (compiled with Clang).

It wouldn't surprise me if there are reasons Zig can do better or makes it easier to use better algorithms, etc, but you wouldn't expect that to show up in something as computationally straightforward as a cryptographic primitive.

By the way, as a C practitioner, I'm a fan of what you're doing with Zig; keep it up! C++, Go, and Rust don't appeal to me for all of the reasons mentioned in your talk.

Re: Zig: software should be perfect [video]

#129

Earlier quoted context omitted.

Because your cpu has a base ISA, plus extensions. The compiler doesn't know if you're going to only run this binary on your machine, or distribute it to others that may share your base ISA but possibly not your extensions, so it takes the conservative approach and doesn't use them unless signaled to do so via those compiler flags. Also, I think -march implies -mtune.

In Zig the default target is native, which turns on all the applicable CPU features. If you want to target something other than the native machine you can use --target-os --target-arch parameters. I haven't exposed extra CPU options for cross compiling yet.

That explains the difference in the SHA256 code case. Case closed ;-).

Re: Zig: software should be perfect [video]

#130
post #92

Earlier quoted context omitted.

> It's too easy to call functions without being aware of the set of possible errors. But that's entirely fine! Programmers are far too obsessed with exactly which functions trigger which errors when it absolutely doesn't matter. All you need to know is what errors you can handle and where in the code you can handle them . If there is a network exception and can recover and retry it at the start of the operation, it l…

> All you need to know is what errors you can handle and where in the code you can handle them. Surely also what errors can occur. Can the network using library throw disk IO errors? Permissions errors? Maybe it handles all the network errors internally to the library and I don't need to deal with those at all.

How do you handle those other errors though? If there is a disk I/O error, you're basically done. Permission errors, same thing. You report, abort, maybe retry.

You don't really need to know in the specific what kinds of errors can occur. If it's possible to recover from an exceptional situation, it's only useful to know if that situation is possible so you can avoid writing code you don't need to. But there wouldn't be any harm in writing an exception handler for an exception that can't happen except for that wasted effort.

Post reply on HN