Hacker News new | past | comments | ask | show | jobs | submit login
Using Zig to Provide Stack Traces on Kernel Panic for a Bare Bones OS (andrewkelley.me)
136 points by andreaorru 10 months ago | hide | past | web | favorite | 42 comments



The more I see of the Zig language, the more I grow to appreciate its beautiful design! Such a perfect blend (IMO; obviously personal preference will vary) of modern language features that encourage inherent reliability and safety of code, with low-level systems programming -- all without the language becoming too complicated or difficult to code productively in.

I'm really looking forward to tinkering around in Zig, for my next hobby project(s).

P.S. In fact, I'm going to 'put my money where my mouth is' and contribute via Patreon[1]. Zig is incredibly high quality and well-designed, despite being achieved from the spare time of so few; it deserves better funding.

[1] https://www.patreon.com/andrewrk


I've been using zig for the past 2 weeks in my spare time. I had so much fun writing in it that I ended up supporting Andrew on his mission and became a Patreon.

In one word zig is "joy". I forget that I am actually writing a compiled language. It feels almost like python. Everything just snapped into place for me.

I encourage everyone interested to give it a try, especially if you prefer C over languages with a strong paradigm. It is very easy to pick up zig. Read the short manual, check some samples or the "std" library and you can be immediately productive.

C interop is amazing. comptime is a sample of how it should work in other languages. syntax is clean and simple. no headers, good error strategies, a (rather) helpful compiler, build system included etc.

The only downside is now it feels heavy to go back to work and hammer on C++ ...


Yeah I like a lot of what I see. The compile-time metaprogramming looks nice and it would be good if I had a project to try it out for :)

Zig Programming Language Blurs the Line Between Compile-Time and Run-Time

https://andrewkelley.me/post/zig-programming-language-blurs-...

Now that I think of it, I wonder if Go could take the approach of Zig for generics, or at least for containers.

Look at how they express List(i32) there. It seems like a much more Go-like approach than say what Swift or Rust do for containers and generics. Like Go, Zig seems to have no classes, only functions and structs.

It does seem like many newer languages have complicated type systems and long compile times. I think Zig has an interesting alternative approach, although I would have to use it more to really make an judgement.


It should be mentioned that Zig's approach is not really generics (at least in the PL sense); one cannot determine the characteristics (or even validity) of `List(i64)` from `List(i32)`, much like that one cannot predict the behavior `f(3)` from `f(2)` for an arbitrary function `f`. This approach, technically termed ad hoc polymorphism (i.e. polymorphism without type system support), is not necessarily bad---as C++ had been fine for decades---but can have some disadvantages w.r.t. compile time (yes, you heard right) or binary bloat.


> This approach, technically termed ad hoc polymorphism (i.e. polymorphism without type system support)

Note that Haskell and Rust (and other languages) have adhoc polymorphism _with_ type system support via type classes/traits. I would class this more as 'duck-typed templates'.

> or binary bloat.

This is an orthogonal issue, to do with whether instantiations of parameterized types/functions are monomorphised or not. I would say the big issue with templates is that they are slow and produce hard-to decipher compile-time stack traces, because they are not checked at the instantiation site.


> I would class this more as 'duck-typed templates'.

Or parametrically polymorphic type variables!

> This is an orthogonal issue, to do with whether instantiations of parameterized types/functions are monomorphised or not.

I stand corrected, as monomorphization is optional for parametric polymorphism but mandatory for ad hoc polymorphism. It is still technically possible to deduplicate similar enough functions, either at the source level or at the binary level, but that is orthogonal to this issue :-)

> I would say the big issue with templates is that they are slow and produce hard-to decipher compile-time stack traces, because they are not checked at the instantiation site.

You are right for the status quo of C++ templates, but I'm not sure for general cases. It might be possible to keep error information down to the instantiation site for better error messages... or not.


> I stand corrected, as monomorphization is optional for parametric polymorphism but mandatory for ad hoc polymorphism.

It's not mandatory for ad-hoc polymorphism. Haskell desugars ah-hoc polymorphism to dictionary passing (although it often does inlining to reduce the overhead of this).


I do not consider type classes as ad hoc polymorphism, I should have clarified this first.


Most people do, though.


>I stand corrected, as monomorphization is optional for parametric polymorphism but mandatory for ad hoc polymorphism. It is still technically possible to deduplicate similar enough functions, either at the source level or at the binary level, but that is orthogonal to this issue :-)

Regarding this bloat thing, are monomorphized functions ever generated for types/signatures that are not actually _used_ in the program?

If not, it's not really bloat, is it? It's what one would do themselves without generics, if they wanted to be able to use them with full speed.


> It's what one would do themselves without generics, if they wanted to be able to use them with full speed.

Right, but in many cases they can be combined without much speed penalty. For example you can combine monomorphized functions when you only rely on methods that are expected to be costly and inlining has a negligible effect. With actual, not ad hoc, generics you may (hopefully) have a control over monomorphization---I think Rust's trait object is a good design for this reason.


It is certainly possible. Concept C++ (the failed C++0x concept proposal) did it; so, to a certain extent, does concept 2.0. Rust which was designed from day 1 to support this also does.


I’m using Zig for Advent of Code and it’s been pretty great. Feels very much a modern C and like it has a good niche having many of the ‘modern’ features I liked in Rust without the strictness.


> modern language features that encourage inherent reliability and safety of code

It's still missing linear/affine types, no? To me that's inexcusable given the current offering.


I wouldn't say it's inexcusable, but I'm definitely trying to avoid any systems language that has implicit null, and allows for data races and de-referencing uninitialized memory from safe code. So that only really leaves ATS and Rust, and rules out Nim, D, Zig, Jai... (for me at least).

Zig certainly has some cool ideas - I definitely think that we should be making the phase distinction more flexible. I do wish however that its compile time function evaluation was built on a firmer foundation, ie. using dependent types.


Please don't spread misinformation. Zig does not have implicit null.

Also depending on how you define "dependent types" - there are a few competing definitions - Zig has them.


> Also depending on how you define "dependent types" - there are a few competing definitions - Zig has them.

How is it ambiguous? Dependent type systems/algorithms can be weaker than desired, but there's no way you can say Zig has dependent types.


I think Andy refers to this [1].

For example you can do:

  const std = @import("std");
  
  fn Int(comptime value: i32) type {
      return struct {
          pub fn value() i32 {
              return value;
          }
      };
  }
  
  pub fn main() anyerror!void {
      const int3 = Int(3);
      const int4 = Int(4);
  
      std.debug.warn("Types: {} {}\n", @typeName(int3),   @typeName(int4));
      std.debug.warn("Sum: {}\n", int3.value() + int4.value());
  }
Output being:

  Types: Int(3) Int(4)
  Sum: 7
Edit: The compiler does the right thing:

[2] https://godbolt.org/z/eLWeU2

[1] https://ziglang.org/documentation/master/#Generic-Data-Struc...


Oh, I must be mistaken then. So pointers are guaranteed not to be null? Can I mark pointers as nullable, and be forced to explicitly check?

Although there are differing ways to define dependent types, and they come in different varieties (dependent functions, dependent pairs/structs, very dependent types, dependent intersections, inductive types), they are all founded on a foundation of type theory. I guess if you want me to clarify, it is 'dependent types based on a well understood foundation from type theory'.

---

Edit: seems like Zig does have optional types! That is a good thing! https://ziglang.org/documentation/master/#Optionals


I love GC enabled system languages since I used Oberon, and beyond trying to fit everything into a 64KB segment, bounds checking was never an issue for the type of code that I write.

So D, Nim, C# (AOT compiled), Java (Embedded/Android Things/MicroEJ), Swift, Go are pretty much on the table as well.


> So that only really leaves ATS and Rust, and rules out Nim, D, Zig, Jai

I don't have a problem with GC so Nim/D are on the table. ATS is a pain in the ass but after the arrival of Rust it's pretty clear that linear/affine types can be user-friendly. There's no excuse any more.


I find Zig much more user friendly than Rust. I have been able to do much more with Zig than Rust in one-tenth the time. I am not writing any code that requires the guarantees Rust may provide, and Zig seems to make it easier to write safer C-type code to boot.


Zig doesn't force you to free all your memory (which occasionally good), so there are whole classes of errors that Rust/ATS avoid that are possible in Zig.


I don't think I'd call Rust user friendly.


If the problem is the type system/borrow checker, it's simply a question of experience.


Not when writing GUI like code, to the point that the Rust team acknowledges that additional work needs to be done after NLL lands on stable.

http://smallcultfollowing.com/babysteps/blog/2018/11/01/afte...

http://smallcultfollowing.com/babysteps/blog/2018/11/10/afte...

So while Rust is much more productive than ATS or Cyclone, there is still room of improvement for that experience.


There's always room for improvement, but speaking of GUI, gtk-rs is pretty usable already.


It is, if you don't mind sprinkling your code with Rc<RefCell<item>> everywhere, as means to access widget struct members in callbacks.


"linear affine types" is an ambiguous term, but I believe Zig has them in the definition that you are using. Please be careful to get your facts correct. Misinformation is harmful to society.


I don't think either "linear types" or "affine types" (they're separate but related things) are ambiguous terms. Under linear typing, values must be used once, and under affine typing, at most once.

Could you clarify the ambiguity you see, and how Zig has them?


> "linear affine types"

It's "linear/affine types," not "linear affine types".

Also: Zig doesn't have linear or affine types. You seem to be quite confused about PL theory, which frankly does not make me any more confident in the language.


A little over the top with this criticism?

Or, the other one: lack of affine/linear types is "inexcusable", really?

As if these are so mainstream that any new language should have them? It's a new language, still in development, use it or not. Zig doesn't want to be a dumpster for trendy PL concepts. It has been designed to be a safer and friendlier C, not a simpler Haskell or Agda.


> Zig doesn't want to be a dumpster for trendy PL concepts. It has been designed to be a safer and friendlier C

Exactly why the project is pointless. It doesn't offer enough beyond C for there to exist any incentive to switch. I would add that affine types are not simply a "trendy PL concept" given that Rust has used them to great effect.


> given the current offering

Current offering being of course Rust.

Rust isn't a "simpler Agda" either. It is a safer C/C++ – as in bare metal capable, no GC, natively compiled… and affine types is what makes the safety possible.


> Rust isn't a "simpler Agda" either.

Calling Rust a "simpler Agda" is a bit insane. Rust simply has a sensible, current type system. Agda is full dependent types.


I really like the look of Zig syntax so far. I had wanted to use this year's Advent of Code contest as an opportunity to learn some Zig, but I had trouble getting the compiler compiled and working.

I see now that binaries are available (maybe they always were?)--maybe it's time to put Zig to the test on these toy problems.


Does anyone know the pros/cons of zig vs nim?


I think that's nothing anyone can say for sure. You need to compare them for yourself and deside if you "need" the one or the other tool for that purpose. Something like a perfect language for all things to do does not exist.


Well, for one thing, zig apparently has no built-in memory allocator?


Kinda, Zig has a small selection of allocators in the standard library, but it would be true to say that it has no "default" allocator. Idiomatic Zig code that requires allocation accepts the allocator interface as a parameter, so that the programmer is free to choose whatever implementation suits their needs.


Worth mentioning that one allocator in the std-library is wrapping malloc.

Here some more details:

[1] https://www.youtube.com/watch?v=WLJ_7lpBhys


Thanks all




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

Search: