Looking at Zig Programming Language
codecs.multimedia.cx
codecs.multimedia.cx
_ = file.write(data) catch |err| { silence_err_somehow(err); ... }.
You can just write _ = file.write(data) catch { ... }.
And the first example of using `catch` in the docs[0] does this. It may be useful to also use the non-error return value of `write` though, because partial writes will return an amount of written bytes less than the amount you asked to write. You need `writeAll` if you want to ignore this concern, and then you don't have to write `_ = ` because the non-error part of the error union is void (so you don't have to use it). Then you get file.writeAll(data) catch { ... }
[0]: https://ziglang.org/documentation/master/#catch[0] https://nim-lang.org/docs/manual.html#lexical-analysis-ident...
WRT having error unions just be a userland tagged union type, here's a reason why we don't do that: https://ziglang.org/documentation/master/#Error-Return-Trace...
The way zig seems to think about errors is that early returns via `try` shouldn't be leveraged as a control flow mechanism to bail and bubble up values across the call stack. I think rather than saying why the error handling is done the way it is, it'd be more valuable if there were documentation on how one is supposed to go about coding to be able to use this bail-to-bubble-up-values pattern. Currently, the workaround is indeed somewhat verbose as it requires mimicking the `try` mechanism (and/or using some sort of global error store or whatever).
The workaround would be to store that info in some sort of global struct (which has all the usual drawbacks of global variables); add a context/diagnostics parameter to your function, or manually manage your own error union in userspace, which means you lose the nice `try` syntax.
throw new Error("Expected foo, found bar at line " + line)
// ...then elsewhere
catch (e) {
log("In file " + file + ":\n" + e.message);
}
In zig, you can't attach arbitrary strings to errors because errors are effectively enums (in the sense of that constant names map exclusively to ints). You can only do this `try callMe() catch return error.Something` (The `return error.Something` part is zig's "equivalent" to `throw error`, but notice how I can't tack on the error message string to the error)`try` only shortcircuits into the `catch return` expression for errors, but for cases like parsers you want to shortcircuit and bubble up error messages simultaneously. You can kinda do weird workarounds by having a pointer to an error field in your input struct and mutating that prior to returning an error value, and/or doing weird nullable-to-sentinel-value-promotion shenanigans with idioms like `callMeMaybe() orelse return someSentinel("Expected foo")` to simulate the early-return-from-expression semantics from `try callMe() catch return error.Something`.
Specifically for recursive descent parsers, I pass around a reference to an regular struct that acts as an ErrorInfo object, and use that to pass diagnostic information up the stack. Control flow is still managed by the error returns, any additional info goes in the parameter.
There is (was?) a proposal to add that capability to the language but Im not sure it had much traction.
And a parse error should (I will argue) not be what Zig thinks of as an error.
There are languages where try/catch became in effect the poor man's coroutine, and it's doubly confusing with parser "errors" because this is not something we need to handle as equivalent to an actual error in our program. A parser error is normal control flow.
I would suggest that in Zig one might use suspend/resume to pass a parser error to something which prints an error, possibly returning with a 'fix' which lets parsing continue and maybe find more errors. https://ziglearn.org/chapter-5/
The other workarounds at least look somewhat transparent about their costs. Even continuation passing style might be something I could grok. But what is the cost of a suspension frame? I legitimately have no idea.
Another issue is that suspend/resume are conceptually highly procedural and I personally find it difficult to reason about in recursive contexts (e.g. can I/should I pass suspension frames around? What idiomatic idioms from other languages does doing such a thing map to? Do I need to manually allocate heap for recursion like I need to for async recursion? Is it reasonable to expect a e.g. golang person to be ok with that sort of stuff?)
Stackful asymmetric coroutines are a good choice, these are stackless but an error is one stack frame so it's unclear that's a problem in practice.
I shrug a little too much when it comes to Zig, although documentation and tutorials improve every time I come back to take a look.
My main point is that a parse error isn't a program error, even if it's implemented with exceptions unwinding the stack for one reason or another (and this should be avoided if possible).
One thing I really appreciate about the language is the emphasis on keeping things simple. I heard someone put it really well yesterday: when you're programming in Zig, you spend your time tackling the original problem, not tackling the language.
Also, the article's complaints may be valid, but most of them are very superficial, which speaks to good language design, in my opinion.
My main issue was around finding good documentation and (not-outdated) examples, which I guess just comes with using a bleeding-edge language. The biggest thing I find lacking is a "Learn Zig" book that takes you through the whole language. Sure, https://ziglearn.org/ exists, but it needs about 10 more chapters. Zig types can get somewhat complex, especially when interfacing with C, for example - I remember some difficulty deciphering those. For many things I ended up just reading Zig's source code itself, especially when looking up standard library stuff.
I mean I get it, who wants to write a book for a language whose features are unstable?
On the plus side, I agree that I fought Zig a _lot_ less than when I first started learning Rust. And I still fight Rust if anything involving generics and/or nontrivial annotations is involved.
Would you say it's as good or better than Lisp?
> and I wish rustc would have a stand-alone assembly files support
Rustc now supports inline assembly and global assembly in Rust 1.59 (the very latest release). So this is now possible:
/// Lib.rs file
global_asm!(include_str!("asmfile.s"), options(raw));
Which will cause asmfile.s to be assembled and included in the rust library.But also, I'd love to see support for C and assembly files in rustc.
It would be interesting to imagine a replacement with a rust equivalent but I can't help thinking that it's difficult or impossible to come up with an architecture / exec format independent replacement (other than m4). gas and cc1as support .macro directives - maybe that would suffice? But compatibility with existing assembly source files would suffer.
I don’t think this has any value.
(Not that there might not be valid criticisms of zig or insights into its value... But this article doesn’t contain them.)
On the first two points, it's disappointing not to see a discussion on the goals of zig and why these choices were made [0]. Like any trade-off, there is a reason behind it.
The third point, which occupies the biggest paragraph, is somewhat irrelevant to me.
An interesting take but which remains at the surface. Might not be worth the 5 minutes it takes to read it.
Did similar thing with rust(ziglang to c vs rust to c++), decided to stay with modern c++ but will keep an eye on rust as well.
I'll have to give Zig a read and make an actual assessment. Glad for the possibilities!
if (optional_foo) |foo| {
doSomethingWithFoo(foo);
}
..could be flow sensitive with optional_foo being non-? inside the block like some languages have done.Well the In-depth Overview ticks all my boxes giving Zig worth a deeper look:
> Zig 0.5.0 introduced async functions. This feature has no dependency on a host operating system or even heap-allocated memory. That means async functions are available for the freestanding target.
> Zig infers whether a function is async, and allows async/await on non-async functions, which means that Zig libraries are agnostic of blocking vs async I/O. Zig avoids function colors.
> The Zig Standard Library implements an event loop that multiplexes async functions onto a thread pool for M:N concurrency. Multithreading safety and race detection are areas of active research.
All variables were pointers; and so a common BLISS statement would be:
.Z = .X + .Y which in 'C' would of course be z = x + y
Z = .X + .Y
Since variables are names of addresses, you have to de-reference the X and Y addresses, add them, and, (crucially), the result gets stored: in the place pointed to by Z --- you don't care what .Z is anymore, as you are about to overwrite it.
When you think of it that way, z = x + y seems 'wrong' because the meaning of a variable name is different on the LHS and the RHS of '='
In many cases, these languages are alternatives to C++ (because of how messy it has become) and are positioned as a kind of "next step evolution" of C that also makes interop with it easier. Though much older, we could put D in that category as well.
I don't think the audience of the newer languages is so much for hardcore C or C++ programmers. The newer languages would be more attractive to newer programmers, those coming from scripting languages that want to learn a compiled language, or older casual programmers actively looking for something different that still has C-like syntax. Otherwise, much older (and battle tested) Delphi/Object Pascal (which is about as powerful and as fast as C++) would be on more radars for switch-over, if not for the excessive C-like syntax bias.
If start moving over anytime some fucker claims ownership of some symbol, you're gonna have a bad time. Much easier in the long term to stick to your guns (eh) and make it clear that you 1 - were there first and 2 - have nothing to do with those little сука
It is the perfect language, don't sleep on it!
Imagine you have to wait for hours for your tests to compile and then to run, and then your micro services to compile to deploy a security update for a major CVS
Rust is poorly designed with poor syntax with a poor foundation that can't scale in today day and age
Imagine trying to rewrite GCC in Bourne shell, or trying to rewrite the Linux kernel as Java. The results are not going to "look better" they're going to be awful, these languages are a terrible fit for the problem.
What people keep finding with Rust is that it's a really nice language to write software you might otherwise write in C or C++. The defaults are correct, the tooling is great, the errors are superb, it's just a very nice experience.
Maybe in a couple of years.
No designer, no localization workflows, no integration with UI/UX tooling, assistive tech stuck in the days Sun sponsored the project, no component ecosystem.
That's the thrust of what's being discussed here, rewrites to another language, the__alchemist says they rewrote from C to Rust and it made their code "much cleaner and more explicit", Shadonototra insists this would happen for any language, and I opined (with some practical experience) that this just isn't so.
Distributed computing stuff in Akka and Orleans.
HPC example? Plenty of stuff converted from Fortran/C LEP days into C++ at CERN during LHC rampup.
My little part was in Atlas-TDAQ HLT.
Also what dependency hell? Rust allows multiple versions of the same crate in the same binary so not everybody has to upgrade at the same time. Eliminates 99% of the hell for me. Cargo is awesome.
Zig aims to be a C replacement from the start, including being a much better toolchain for cross-compiling C code than any other C compiler.
For embedded development, which is what is relevant to me, I never found D a good option. If that has changed they need to work on the marketing.
As an object-oriented garbage collected language with various nice features, I don't see what sets it significantly apart from C# anymore. Well, you can compile proper binaries. But dotnet core interpreters seems to be spreading on our Linux boxes, so then being able to run the same .exe in Linux and Windows is actually a nice thing. Then again I'm not in the market for such a language, so I probably shouldn't comment.
D is a natural evolution of C, fixes its issues (bounds checking and use after free for example), and adds some niceties that makes it pragmatic and polyvalent on top of a solid alternative to MACROs and metaprogramming capabilities
You can do native system development, embedded development, high-level tasks thanks to the optional GC and it can also becomes a scripting language
Something you can't have with Zig, but that's not its goal anyways
Also D is one of the rare languages that maintains its own backend, its own compiler (with optimizations available!) and a community that grows organically without needing major corporations support, it is a true labor of love, and it still gets new features today (ImportC)
Why i stick with D? because of all of the above! it's an unmatched language, that compiles code SUPER FAST!
.. and the syntax remains similar to C/C++; no need to learn new syntax gymnastic
Where with D, it seems to just be sitting in a corner, waiting to be noticed as a C++ replacement. But, people are not checking for D and hearing little about it. There is a long list of interesting languages, but if people don't see an almost immediate ROI for learning them, they will just sit and collect dust. Even if a language gets a bit of publicity, it still takes luck, timing, or corporate interest to move up the ranks.