I sincerely approve of the skepticism. I can assure you there is already a very large fire under my ass to get this shipped.
To provide some more context, here is a snippet from the latest release notes[0]:
> Having a package manager built into the Zig compiler is a long-anticipated feature. Zig 0.8.0 does not have this feature.
> If the package manager works well, people will use it, which means building Zig projects will involve compiling more lines of Zig code, which means the Zig compiler must get faster, better at incremental compilation, and better at resource management.
> Therefore, the package manager depends on finishing the Self-Hosted Compiler, since it is planned to have these improved performance characteristics, while the Bootstrap Compiler is not planned to have them.
I can't help but mention, I am incredibly excited about how well the self-hosted compiler is coming along. Progress is swift, and all the ambitious design decisions (fully incremental compilation[1], extremely fast debug builds[2], low memory usage, multi-threaded) are intact. I anticipate it to make quite a splash once it becomes generally usable.
I don't expect the package manager to take nearly as long to develop.
[0]: https://ziglang.org/download/0.8.0/release-notes.html#Packag...
[1]: https://vimeo.com/491488902
[2]: https://twitter.com/andy_kelley/status/1416485475125141504
I'm going to check out Zig because of your response.
I love coming across comments like this one on HN.
I wrote about cross-compiling to a raspberry-pi and writing a barebones driver for an OLED display[^1]
That was a lot of fun despite the fact the code was incredibly basic. The interop with C was effortless.
[^1] https://www.kamelasa.dev/posts/gettin-ziggy-with-it-pi-zero....
I feel Elm's growth has been stunted because the creator is actively hostile with whoever disagrees with him. I remember the infamous "leaving elm" post which was handled so poorly by Elm's author.
If they had handled it well, elm would have definitely seen a large uptick in adoption
There's very little evidence of ongoing hostility from Evan - I pay attention to his work and writings and find he's rarely if ever hostile. In fact he's very mild-mannered, and is painstakingly detailed about explaining the reasons for why he does things the way he does, over and over again.
Perhaps that wore thin once or twice in the face of repeated criticism and demands. We've all seen way worse.
Yes, sticking to his vision has excluded ideas and contributions, and made some people very angry. There have been several high profile blogposts and twiits painting him as some kind of despot. And when other overzealous developers swooped in to defend his work, it just fed the meme, one which people on HN like to repeat every chance they get.
Well, I for one am glad he's a 'code despot' - considering how many people keep demanding that they dilute Elm's guarantees for the sake of JS library interop or marginally less boilerplate. Any bending to these demands and Elm today would be just another framework with a slightly different syntax, with none of guarantees that allow for the innovative tooling and libraries [1] we see coming out of the community.
Whether you agree with Evan's vision or not, Elm is doing just fine meeting its stated goals and is a fantastic and delightful technology and toolchain.
So please give over with trashing other people's hard work. If you don't like it, just don't use Elm.
[1] for example, the compiler's dead code elimination capabilities, elm-review's tco detection and other amazing auto-fixes, lamdera, etc.
However, elm is not pragmatic. the issues mentioned in Luke plant's "leaving elm" [0] are valid. and Evan's response to that was far from satisfactory[1] and this has affected Elm's popularity.
The disagreement between BDFL and community has happened in many other cases as well. and there is a way to solve them amicably. Most recently, Vue faced it when community caused uproar over composition API RFC. and Vue solved it nicely and amicably[2]. If Elm has followed similar approach, Elm too would be praised for it, and gained even more popularity.
[0] https://lukeplant.me.uk/blog/posts/why-im-leaving-elm/
[1] https://www.reddit.com/r/elm/comments/g070sz/evans_response_...
The criticism is valid. Haskell is painfully strict and, still, it offers various ways out of its guarantees. Rust is painfully strict and gives you `unsafe`. Neither of them break your build if you don't host your code in a specific github org.
Limiting certain kinds of js access to vetted libraries is benevolent, if that's part of the language's goals. Likewise the lack of type classes or mutability/unsafe escape hatches.
Elm's developers do not enforce constraints to be 'hostile' or 'far from benevolant'. It's a tradeoff - language power for certain guarantees, guarantees that not even Haskell can provide.
Unfortunately, this sometimes means trying to constrain access to the completely unconstrained js library ecosystem, so the solution, at least for now, is a bit of a blunt weapon.
Note that Elm doesn't 'break your build' if you don't use github - that's a constraint on linking to official libraries only. I don't use github and my code works just fine.
ahem
> Progress is zig ...
Thanks for all your work! I ported a bunch of basic data-structures from C++ to Zig very early on (right as y'all began exposing generics). Even though the code no longer compiles, still worth learning The Zig Way™.
If I may, my only piece of feedback (as someone who looked at Zig with fresh eyes) is that the syntax has lots of special chars (@, !void, .{}, &, etc). Is there a rationale published for these and other design choices (even if it's "I like it that way"). I would have expected a language with ambitious goals like Zig to take a stab at coming up with a grammar that's even simpler and cleaner than C.
Regardless, getting a new programming language working, noticed and used is monumental effort, so I wish you best of luck with Zig!
Relative to c, though there are imo fewer special chars:. Zig adds ! and ?, Which are there for really good reason, but subtracts for example <> and #.
The hardest thing is the . syntax for anonymous structs and tuples, but to be fair it is very consistent, and so many times I'd think,"hey I wonder if..." And then try it with the syntax and of course it works.
That is sadly my gripe with a lot of the newer languages too. I have been looking for something to replace C for a long time, something that makes it easier to not shoot yourself in the foot. I just find most of them significantly less readable than C.
Rust seems to be going down the route of C++, hence I do not deem it a safe language at all, as I have an exceedingly hard time parsing the source and figuring out what is going on.
Zig is WAY better in that regard, but as OP mentions: all the special characters make things more difficult to parse. Calls like these (taken from the introduction page) contain so much visual clutter:
try expect(@call(.{}, add, .{3, 9}) == 12);
I sadly doubt any of this is possible to change at this stage anymore. Zig definitely seems the most promising C replacement with regards to tooling, especially cross compiling and C interop are a breeze. Truly inspiring work!The only new C-like language that really nailed it for me syntax wise would be Nim. Very clean and easy to reason about, which - from a programmer perspective - makes it exceedingly safe to program in. It just seemingly lacks most of the safety guarantees that the compilers of other, newer languages provide.
To make things a little more interesting though, Zig's shape in this regard should be roughly congruent to C's, so you should be able to do anything with Zig that you can with C. This includes linking against shared objects for C libraries like, e.g., SDL2[1] or building mixed-language Zig+C projects[2].
0: https://ziglearn.org/chapter-3/#packages 1: https://dev.to/fabioarnold/setup-zig-for-gamedev-2bmf 2: https://tiehu.is/blog/zig1
https://github.com/ziglang/zig/blob/master/CONTRIBUTING.md
Though I’m interested in hearing from the creator itself as well.
How will you verify the chain of trust?
The only thing I've found being a pain point is C's biggest pain point: the lack of a proper string type. Even though I've worked for years in the past with C and C++, I still get tripped up, and in Zig I keep on being confused whether I should use raw byte arrays, or sentinel-terminated arrays for certain string manipulations. Maybe I'm not familiar with common idioms in Zig that might help avoid this C-style tracking of string/buffer lengths malarkey that I tend to end up with where a slice is not appropriate.
Maybe a dynamic-length string library is the way forward.
I've always tried as much as possible to treat strings as just opaque data and never look into them, which tends to work well, but in some domains you really need to look at and massage the characters/codepoints/grapheme clusters, and the lack of a first-citizen UTF-8-aware string type is, I think, a bit unfortunate in this day and age. I understand having one of those could make C interop a bit gnarlier (I think Odin's approach of having two separate string types -- one of which is just meant to be used for interop -- should be workable).
Oh, and the ecosystem of libraries is still young. I needed to format timestamps as human readable date/time to a file, and had to step down to C to do something that basic[1], but it'll get there in time, I'm sure.
[1]: "basic" from the perspective of the end-user of the language. Having written a (not even fully featured) date/time library in C++, I consider date and time to be one of the hardest domains I've ever had to work in.
Then again, I'm not sure exactly what a type would be for Unicode. Basically just a flag that the array of bytes has been checked and is valid? It's nice to let the type system help you enforce your application boundaries, but I think you could wrap it pretty easily in a struct of your own if you needed that.
[0] https://github.com/ziglang/zig/blob/master/lib/std/unicode.z...
In something like Zig I think it's OK that it's merely a stated assumption that these bytes are UTF-8, not actually checked - so long as people take that seriously.
It's nice in code that maybe isn't very concerned with such things to be able to know that any "string" is actually text we can display, output to a console, something like that. Not for example a TCP/IP packet we haven't decoded yet, or the first 32 bytes of a JPEG image.
Programmers who aren't writing firmware for a vacuum cleaner or washing machine, probably want string literals like "this" in their code, and you'd naturally want those to have a type, for which a string type is the obvious fit. I believe in 2021 this type should obviously be UTF-8. "It's just some bytes" is far from useless, but it isn't much of a string type.
I was sceptical at first about Rust's choice to build in str (immutable UTF-8 string slices), because that seems like a relatively high level concept. But unlike std::string::String, str isn't that tricky after all. See, the only place such things would come from (not having std::string::String to make new ones) is the program source, and our compiler is necessarily already reading the program source, so it follows that the compiler must know how the source is encoded etc and thus it does actually know exactly what those literal strings are. If your literal wasn't Unicode, that wasn't a Rust program and it won't compile.
And also as long as all supported operations on those bytes still result in utf-8 strings (there could be unsafe operations too, like splitting at a random byte offset, but there should be a clearly marked safe set that wont corrupt a valid utf-8 byte sequence).
I have this internally in my lang AST Rust
Utf8
I think that is the type: struct City {
name:Utf8
}
For a scripting lang like mine I surface it as `Str` but in a system language I think is better to say `Utf8`Well, as you said "Unicode is quite large and complicated".
And in 2021 it's not just anglosaxon software users anymore (if they ever were), good unicode lib if not type is a must.
You don't need a string type for that, you just need routines that handle UTF-8 strings, like utfcpp (https://github.com/nemtrif/utfcpp).
However, if the philosophy here is to be unopinionated and optimize for flexibility over everything else, I feel I can't really fault them for making that choice.
Like in any language with any lib that is not self-contained (cuts across all aspects of a program and can be used anywhere) and has many different implementations and no sanctioned one.
I was doing some pretty gnarly high-level filesystem work (transforming an unstructured tree of files/folders into a more structured tree following certain naming and categorization conventions -- a lot of parsing/building/concatenating paths). I have been working with higher-level languages for the past decade or so, but have had a wanderlust to get away from garbage collected langauges and go back to my roots a bit, so I've been comparing some of the newer options in the "improved C"/"C++ replacement" space.
It may very well be that I'm not the target demographic for Zig, and that's totally fair, but to me it felt pretty jarring to go back to working with C-style character (byte) arrays and all the (well-known) issues with which they are fraught. I didn't feel like Zig was an improvement over C in the string handling area (as opposed to all other areas), whereas languages like Go and Rust are.
Other than that, I felt Zig was a joy to use.
Rust's OsStr is my favorite approach so far. It stores Linux's raw bytes as-is, and stores Windows's possibly-valid UTF-16 as WTF-8. This makes path management "just work", with the ability to operate normally on invalid UTF-8 or UTF-16 paths, and zero-copy conversion from UTF-8/ASCII strings to OsStr (though converting OsStr into UTF-16 requires parsing). (Qt's QString-based file dialogs on Linux fail to convert invalid UTF-8 paths like those in https://github.com/petrosagg/wtfiles into QString, causing Qt-based apps to open/save the wrong paths.)
However there are difficulties in printing an OsStr. For example, a file dialog that shows filenames as raw bytes can't show non-Latin/Unicode characters in a human-readable form, and a file dialog that shows filenames as Unicode strings can't handle invalid Unicode filenames. GTK3 file dialogs show filenames as Unicode strings, and when encountering files with invalid Unicode names, instead displays "file�name.txt (invalid encoding)".
Worse yet, how should a file dialog allow users to rename files? If it's based around byte arrays, the user can't enter Unicode characters directly, and if it's based around Unicode (or a locale-specific text encoding), it can't display existing files with invalid Unicode/etc. in the name (probably not an issue if it allows the user to rename to a valid name), nor allow users to enter invalid Unicode (which is not an issue IMO).
APFS is utf8, HFS+ is utf16.
> with valid Unicode glyphs
That doesn’t really mean anything.
Apple’s fs do guarantee the paths are correct, as in, valid whatever encoding this has nothing to do with glyphs.
APFS also does not perform any normalisation while HFS+ uses a custom variant if NFD. While HFS+’s normalisation has its issues and critics, APFS’s lack of normalisation is probably worse: https://eclecticlight.co/2017/04/06/apfs-is-currently-unusab...
For a minimal std, maybe Zig can have a standard `str` type which is used for all functions that don't need to grow the string and the `String` type is provided by any 3rd party library which is expected to provide a conversion function from `String` to `str`.
These strings types could or could not have a requirement of being Utf-8. Maybe there is a builtin wrapper to convert them. So you can any dynamic byte array which can be converted to `str` (just a pointer and length, no validation) which can be later converted to `utf8` (str but validated). A `String` which guarantees Utf-8 could directly convert to `utf8` to save validation costs.
I don't think that's the parallel I would draw.
Rather, I think both Zig and Rust aim to serve the use cases C and C++ do, but Zig and Rust pick different points on the tradeoffs involving safety. Zig feels like a "better C", in the sense of bringing modern language features to C, but it chooses safer rather than safe. Rust supplies modern language features as well, and chooses to prioritize safe; sometimes that comes at the expense of other factors, such as productivity or compile time. I personally prefer the point on the spectrum that Rust chose, but I think Zig still offers improvements over C.
One thing that impressed me about Zig is how simple the language is. I get the impression that it would take very little time to properly learn the language and become effective at it.
Rust, on the other hand, has a whole lot of complexity even without the ownership semantics. So much to learn and understand. And, in some cases, work around. It seems, from looking at others' code, that there is a very strong temptation to lean on `unsafe` instead of figuring out how to solve a problem within the constraints it normally applies.
That has me wondering if there's really all that much safety difference in practice? Simpler code is generally easier to understand, and it's generally easier to find and fix bugs in code that's easier to understand. I can imagine a world where it's a tradeoff between, "You definitely won't have any of this relatively narrow category of bug, unless of course you opt out of the static checks, which you will probably do sometimes, even though we tell you you shouldn't," and, "There are no particular guarantees, but you're generally less likely to have any of this broader category of bug."
Some people do believe that, let's say "conceptually parsimonious" languages (using complicated words to describe simplicity is amusing to me, sorry) do exactly what you say. Others believe that complexity inherently exists, and you can put it in the language, where a compiler can tirelessly check certain properties for you or a runtime can do magical complicated work on your behalf, or you can put it in the programs, where users have to check such things themselves.
Another way I heard this expressed one time is Ruby vs Python. I once heard someone talk about how they preferred Python as a language more, because it was simpler, but enjoyed actually programming in Ruby more, even thought it was more complex, and they value simplicity. The reason is that all of that nasty ugly stuff Ruby lets you do makes you be able to make extremely nice APIs. Things you couldn't do, or just folks don't do, in Python. And therefore they ended up liking using Ruby more in practice.
I suspect that this is a topic that we, as a profession, will debate about endlessly.
I think we'd all agree there's a spectrum, with, say, Assembly on one end and Idris on the other, where neither Zig nor Rust are near either extreme, and are actually rather close to each other. It's not a matter of accepting or rejecting a principle, but on picking slightly different sweet spots (hopefully) on a tradeoff spectrum.
Rust doesn't check at compile-time most things that could be checked at compile-time. In fact both language check almost the same things at compile-time, and almost the same things at runtime. The design differences come down to one or two things that are clearly tricky and that neither language "does for you" -- i.e. you still need to think about them -- but are, indeed, checked in different ways.
I personally would not agree with
> Languages with more powerful abstraction abilities bring joy to the writer but make things tedious for the reader.
I find map/filter to be easier to read than a C-style for loop. Things that are conceptually denser make discussion between experts easier, even if it makes things harder for non-experts. This is why jargon exists, for example.
In short I believe languages that one can pick up in a couple of days and then read code where reasoning of the code is highly local would stand a higher chance of being perceived as simple.
Programmers notoriously conflate "simple" and "easy" (classic talk: https://www.infoq.com/presentations/Simple-Made-Easy/) and so I believe languages that are easy for a lot of programmers will also be perceived as simple, whether or not that's accurate.
char* c_foo(char* bar) { ... }
simpler than fn rust_foo<'a>(bar: &'a str) -> &'a str { ... }
?On the one hand, c_foo is shorter and doesn't use strange symbols. On the other hand, rust_foo doesn't leave you guessing on whether the returned value needs to be free()d or not, and whether the heap allocation backing `bar` can be free()d after the call or not.
fn rust_foo(a: &str) -> &str { ... }
but Arnavion is trying to be explicit here to make a steelman.)Are you sure those examples of unsafe Rust code were written using unsafe in order to work around safety constraints due to difficulty?
I ask because there are some very real and valid use cases for unsafe code in Rust that have nothing to do with the fact that writing the code safely might be more difficult.
The big bias I'm working from here is that, as an outsider, I have a hard time reconciling all the big talk about how Rust makes it impossible to experience certain classes of bugs, with the existence of a known set of things that its type checker can't check, and which includes a lot of things that are difficult to avoid. One could be forgiven for thinking that the Rust community has a habit of keeping its fingers crossed behind its back when talking.
I don't want to make the perfect the enemy of the good, of course. I realize the feature is there for an important reason. But it sure looks to me like Zig is also good despite being imperfect. Perhaps they're both similarly good languages.
https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
The spec here is to turn one mutable slice into two non-overlapping mutable slices. The operation does not violate memory safety; we wind up with only one mutable reference to any part of the object. But the compiler does not know how to verify that the programmer has done this correctly. Here, 'unsafe' means 'I certify that this is safe, use that as a lemma to do further checking'.
It is a big difference to have it on your face that something might need to be properly double checked and having each line of code as possible security exploit.
Would you consider Oberon to belong in the former or latter category? On one hand, there's no explicit code blocks as such (syntactically). On the other hand, unsafe primitive operations are limited to the SYSTEM module, so functions calling operations in the SYSTEM module can be considered doing unsafe things.
Just like Modula-2 already does it.
And just like languages with unsafe code blocks, there are compilers that have switches to forbid the use of tainted modules unless explicitly allowed.
In any case, way better than what C, C++ and Objective-C offer, regarding security per line of code.
It's far worse than that, the problem is that the machine can't know what was intended. The result 5 from calling sum([1,2,1,1]) makes sense, but alas this function was intended to produce the product not the sum, the inputs were supposed to be [1,2,3,4] and the desired result was 24... the machine can't hope to guess that.
A program may be buggy even if it its meaning is transparent and exactly reflects its effect, simply because it doesn't reflect the intention of the person who wrote the wrong program.
This quote is attributed to Babbage:
'On two occasions I have been asked, – "Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?" ... I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question'
But that is what we wanted, even if we obviously can't have it.
The biggest difference in the problems they solve, in my view, is that Zig does not aim for safety. It'll be easier to write safe programs yourself with Zig than it might be with C, but it doesn't give you any guarantees.
Zig also seems like it may be a better fit for embedded programming; there's a big community around embedded Rust, and it sounds like there's a lot of progress, but still an uphill battle.
But yes, Zig:Rust :: C:C++ has merit in that Zig is a much smaller, less feature-rich language.
The issue with this analogy is that it assumes that C, one of the languages used in the most diverse set of circumstances ever, means the same thing to everyone. Same with C++, frankly.
There's definitely something to that analogy, but I think it also misses a lot of really important details. For example, Zig has generics, which right off the bat makes it hard to say that it's "like C". Also Rust enforces memory safety, which isn't like C or C++.
Of couse, it's a much less powerful feature, there's nothing like interfaces or stuff, just a way to "overload" based on the type.
The only preprocessor part in the example is mapping the function-like macro 'cbrt(X)' to the language keyword '_Generic(X)'.
I wrote a blog post about comptime if you want to learn more: https://kristoff.it/blog/what-is-zig-comptime/
I feel like Rust wants to replace C but it also wants to replace C++. And given the complexity difference between the two languages, that means that Rust will end up closer to C++ than to C.
So there's some overlap based on how Rust positions itself, but not based on how Zig positions itself.
Rust wants to make systems programming memory safe (and type safe while at it). It's not really about any specific language, it's about dragging the field forwards on the safety front.
Well it's not really Rust, it's the Rust community, which kinda wrestled it away from Graydon Hoare: the original inception of Rust was more of an applications language (what with the evented model and the split stack and the planned-though-never-really-implemented GC'd pointers) — which explains part of the historical confusion with / comparison to Go.
But then a critical mass of people took a look at this early rust and figured "we've got plenty of memory-safe and type-safe (ish) languages for applications, but this thing has precise control over memory and lifetimes and shit, we could actually build reliable infrastructure with that", and it coincided with a lot of memory safety issues news (which has been continuing ever since), and the rest is history, pretty much.
> Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
Keep up the good work, I'm excited to see where Zig goes next.
I'm imaging some poor software engineer in 2065, having to maintain an enterprise legacy stack... levels of FORTRAN/COBOL->C->C++->ZIG->RUST->PERL->PYTHON.... all the way up....JS..etc..
Of course, the complexity might explode from there…or you might just not really notice it all that often. Anybody familiar with LLVM or WASM or heck even a language that can transpile to JavaScript can probably relate to this possibility.
The thing that’s harder to fathom is going from low-level to high-level. High-level -> intermediary low-level -> high-level is fathomable though.
I do, because that would mean that more attention must be paid to make languages work together, instead of each language ecosystem becoming its own little island. Maintaining a project built from a dozen languages (where each language does one thing well) should be just as simple as maintaining a mono-language project.
I have good news and bad news: That programmer will not know any of those languages, as they will just program in English and GPT-3000 will convert it into code.
GPT-3000 will have no problem understanding some interpretation of your English (or Spanish or Mandarin) "program", but will it be the right one? How long will it take to get GPT-3000 to understand what you mean when you can only talk to it in English (or Spanish or Mandarin) ?
A piece of software is a set of instructions that tell a computer how to do something. We use languages like C, Python, Haskell and Lisp to describe what to do in ways that avoid as much ambiguity as possible.
The question is: if you used a natural (human) language, how much confusion would (a) the putative GPT-3000 (b) another programmer (c) a compiler/interpreter incur when trying to understand your instructions?
The ZIG compiler requires a C compiler to compile to a native executable though.
Do you mean compiling the zig compiler or zig programs?
I think neither is quite the case. zig itself can build both Zig programs and C/C++ ones. Zig doesn't need to use a C compiler to build native Zig executables. It does need a linker, as does Rust, as do C/C++ programs and AFAIK zld is not yet ready.
However it's accurate to say that it's only able to compile the C/C++ code by leveraging libclang, the C/C++ compiler underneath.
By the way, check out this project by Zig core team member, Vexu: https://github.com/Vexu/arocc
No it doesn't? Not unless you want to also compile C, which the Zig compiler is capable of doing because it bundles clang.
> soon we’ll also have a package manager
This. If they succeed to do this (and I have my doubts) it will be a paradigm shift for low-level "system" programming.
Let's hope this is not vaporware.
Finally, it's relatively easy to contribute to Zig. Andrew Kelley is very opinionated on where the language should go with an emphasis on simplicity which sometimes can make things awkward but he is also persuasive and welcoming. I have been using Zig for a month and I am still positively surprised so he must be onto something.
[0]: https://github.com/ziglang/zig/blob/master/lib/std/json.zig#...