25 ms·
Failing to Learn Zig via Advent of Code
- Comevius 5y agoI see what went wrong here. You tried to lean on documentation and Google, you should have just go for the source code. It's quite readable, and it's the recommended way to learn for now. Zig has a few peculiarities, it's not stable yet, you kind of have to follow it's development. https://github.com/ziglang/zig/tree/master/lib/std https://github.com/ziglang/zig/tree/master/lib/std
- meheleventyone 5y agoYeah I did AoC a few years ago in Zig and found sticking the std path in my project so I could quickly search it was the best tool to figuring out how to do various bits and pieces.
- rhodysurf 5y agoI did the same! Cuz at the time there was no language server available beyond syntax highlighting and linting. But now there is a pretty good language server package available which is really helpful giving autocompleting
- coldtea 5y ago>I see what went wrong here. You tried to lean on documentation and Google, you should have just go for the source code. That sounds rather like a different thing was wrong - not where the author tried to lean: Zig has crap documentation.
- bollu 5y agoZig documentation isn't crap; the documentation *does not exist* yet. There's an important difference between the two which seems to be ignored. Zig is young. They're spending their time maturing the core ecosystem --- the compiler, the standard library, and so forth. It's perfectly reasonable to expect early adopters to "RTFS/read the fucking source". *When* they start writing documentation, I'd be perfectly happy to hear comments like "The zig documentation is crap". At this stage of the project, I'll be honest, I find it upsetting to read comments like this.
- rootlocus 5y agoZig absolutely has documentation: https://ziglang.org/documentation/master/ https://ziglang.org/documentation/master/
- delphLonepaw 5y agoThat's the language reference, that also links the experimental autogenerated std documentation Right now, Zig is young and the documentation is kinda ok (Language reference is clear enough, outside a few things that need better visibility, for example, explaining concepts that are only on the examples can be lost easy), the autogenerated std on the other hand used to work meh, until a commit broke it afaik, so rn is in a pityfull state, but there is no use in reworking it until stage 2, so that's that
- rootlocus 5y agoWell, the STD documentation page has a red banner saying it's experimental and you should consider reading the code (which worked great for me) so at least you have a warning.
- coldtea 5y ago>Zig documentation isn't crap; the documentation does not exist* yet. There's an important difference between the two which seems to be ignored.* Well, crap as an offering (what you get atm), not necessarily as in the quality of what little it is. It does though have a reference and documentation page: https://ziglang.org/learn/ https://ziglang.org/learn/
- kristoff_it 5y agoThe autogenerated docs immediately greet you with a red banner that states These docs are experimental. Progress depends on the self-hosted compiler, consider reading the stdlib source in the meantime. It also links to a page that explains how the standard library is structured: https://github.com/ziglang/zig/wiki/How-to-read-the-standard-library-source-code https://github.com/ziglang/zig/wiki/How-to-read-the-standard...
- andi999 5y agoRecommended way to learn for now? Any sources for that?
- delphLonepaw 5y agoRead the language reference, use ziglings, use ziglearn.org, use zls (don't trust it too much tho, but it will help a bit) and vscode or any text editor that supports jumping to the source, and of course, RTFS
- rootlocus 5y agoRead The Fabulous Sources?
- delphLonepaw 5y agoThat's a way of reading it
- mst 5y agoWhen saying it positively I often say UTSL instead (Use The Source, Luke).
- andi999 5y agoIsnt the problem with that approach you have a model of the implementation (and not of the 'interface')? I mean you dont know if you are useing the function as intended and if not used as intended it might (actually will) break in the future. For auditing you are right, of course.
- mst 5y agoNot exactly. I mean, as a first note one of the ideas here is reading code already written in zig to understand how to write code in zig - so in that case you're effectively inferring the interface from what the authors of the code itself are treating said interface to be. But also, interface documentation is pretty much never truly complete because there are almost always some implicit assumptions involved (and if you try and make all of those explicit you rapidly end up with documentation that's so verbose people's eyes glaze over when they try to read it so their model often ends up incomplete anyway so how explicit to be is itself a trade-off). Then zig embeds its test cases in the source file, so you can look at what the authors have explicitly declared -must- work to help you know what the interface is intended to be. Plus when I'm source diving, I've done enough of it over the years to be able to at least attempt to build up a model of not just the implementation, but of the author's mental model as they were implementing it, and if you can figure out their intent, it's much easier to guess what their code is -meant- to do and thereby what it will hopefully continue to do into the future. If in doubt, though, leaving a comment in your own code as to what assumption you're making -and- writing a unit test in your own code that verifies the assumption continues to hold will mean that at least if it doesn't you'll see a test failure in your own suite that tells you it changed. (or: "code to the interface, not the implementation" is absolutely the right thing to aim for, but in practice the line between the two is fuzzy and cases where you have to make a judgement call will always show up eventually)
- ofrzeta 5y ago> My biggest failure was a poor decision to solve all Advent of Code puzzles in both Rust and Zig. That's a bit overambitious maybe.
- skywal_l 5y agoYes, error messages in zig needs improving but that's not surprising. Rust error messages where pretty terrible back in the day. I found zig language reference pretty good[0]. It is simple, lot of example. I would find 80% on there and had to google the rest. And as another commenter said, looking at zig source code is actually not a bad idea. The std lib is pretty clear and with comments. My biggest beef with zig is a lack of a package manager. But apparently, it is high in the list of priority for the author so... [0] https://ziglang.org/documentation/master/ https://ziglang.org/documentation/master/
- nkozyra 5y agoI think zigs documentation is wonderful and explains most seemingly esoteric syntax and language options really well. But if you hit a roadblock it's hard to track down more info. Some of that is due to the lack of adoption, though.
- the_duke 5y agoA potentially huge roadblock to better error messages is lack of generics and interfaces/traits/classes. The comptime machinery is really cool, but it gives the compiler much less information to work with for producing good errors. It'll be interesting to see how this plays out as the ecosystem grows.
- naasking 5y ago> The comptime machinery is really cool, but it gives the compiler much less information to work with for producing good errors. Why do you think that's the case?
- jibalt 5y agoBecause all the semantics for what are the valid parameters for types and other comptime operations are scattered throughout the code that is run at comptime. It's the same problem as with C++ template code, but perhaps worse. For instance, if you do a formatted print and pass an integer to a "{s}" descriptor, the std.fmt code calls @compileError with an informative message, then a dozen lines of stack trace are printed before a line that shows the source code line that invoked the print, and then yet another stack trace because std.fmt returns an error. And that's for std.fmt that is well behaved and calls @compileError when it should ... in many cases the error will be due to a type mismatch or invoking a non-existent member, etc.
- p0nce 5y agoSuch usability reports are super informative for when you want to make software usable.
- izhak 5y agoIt starts with a mention of hating Python and continues into a detailed report that could be alternatively called “100 wrong things that Python got right”
- keyle 5y agoI appreciate the honesty and thoroughness that the author is displaying here. There are too many posts that focus on successes and embellish the state of affairs. That said nothing was too surprising about a language that hasn't hit 1.0 yet.
- brabel 5y agoNice to see a "brain dump" as someone who learned many languages and can easily relate with most of the issues the author faced. But I take issue with this: // Obviously good and easy to read return a*(1.0-t) + b*t; // Obviously bad and hard to read return add(mul(a, 1.0 - t), mul(b, t)); Sorry, but the first one is not obviously good, it's just what you're used to (the second one is indeed bad). Here's what I would consider obviously good :) for the objective reason that it does not require difficult to track implicit rules about operator precedence: 1, - t, * a, + (t, * b) I invented this syntax myself :) but it's extremely obvious once you know how it works. It should be clear that this is similar to concatenative languages, where you push values onto a stack then apply operations on the stack. The `,` is used for pushing to the stack, basically. But then, it also allows you to "mix" more standard function notation into it... so when you write `a b` that means calling the `a` function with `b` as an argument (think LISP). Now putting everything together 1, - t Should read as `1 minus t` as `- t` is the function `-` being called with one value from the stack, `1`, and one from the "function call" (as if it was LISP `(- 1 t)`). Next: 1, - t, * a Reads as "1 minus t times a" and has no ambiguity: the result of `1, - t` is the first argument to ``, the second argument is `a`, so you get LISP's `( (- 1 t) a)` but reads much more naturally. Finally, at the end, for readability, we use parens to group the final part of the equation: 1, - t, * a, + (t, * b) Hopefully it's obvious what happens now? Does anyone like this form of expression or is just me? I tried to mix the best of LISP with the best of FORTH :) and I really like this.
- alexvoda 5y agoThe notation the author complains about looks like a broken polish (prefix) notation with added parens. Your notation looks like a very bizare mix of infix arrangement postfix (RPN) behaviour plus added parens for good measure. RPN has plenty of benefits, once you get used to its initial awkwardness, that I feel your notation fails to capitalise on.
- ReleaseCandidat 5y ago> The notation the author complains about looks like a broken polish (prefix) notation with added parens. The notation the author complains about is the first version using 'normal' (prefix) functions. a + b = (+)(a, b) = add(a, b) = a `add` b (Haskell infix) = a .add. b (Fortran infix) a * b = (*)(a, b) = mul(a, b) = a `mul` b = a .mul. b so a*(1.0-t) + b*t is (+)(a*(1.0-t), b*t) = (+)((*)(a, 1.0-t), (*)(b, t)) = add(mul(a, 1.0-t), mul(b, t))
- ffhhj 5y ago> Rust is trying to be a better C++ > Zig is trying to be a better C I recently started learning Rust by adding snippets to The Quick Snippet Reference [0]. Without any previous knowledge of the language I knew there had to exist a data type for dynamic arrays. After several Stack Overflow searches I found it: Vec. It was very interesting to compare to Python's Array. Getting to understand the drain() method was a refreshing experience. I'm still hesitating about the best way to handle errors (Rust has no exceptions, just panic!), but that will probably become clearer when working with threads. And Rust compiler's warnings and errors are really nice. I'll certainly give Zig a try. [0]: https://github.com/snippetfinder/The-Quick-Snippet-Reference https://github.com/snippetfinder/The-Quick-Snippet-Reference
- pyjarrett 5y agoHow is this different from Rosetta Code? (https://rosettacode.org/wiki/Rosetta_Code https://rosettacode.org/wiki/Rosetta_Code)
- ffhhj 5y ago> How is this different from Rosetta Code? Yeah in some way they are the same kind of reference. I already knew about Rosetta Code, explored their snippets and found them valuable as a learning tool by curiosity. But The Quick Snippet Reference presents examples to do the smallest tasks that we would need when programming. The tree of these minimal operations is shared between languages, so it's easier to compare, or find how to do something in a new language that you already use in another.
- tialaramex 5y agoThe important decision about an error is: Should I cope with this? If so, do so. This is likely if you're a library and unlikely in some application software, especially AoC. Don't ask me to try calling fooA() and then call fooB() if it fails, just have that code inside fooA() already. If not, might my caller want to know what went wrong? In a library your users might want to cope, unless you're sure the situation is fatal and all hope is lost, so always return Result and use appropriate Errors for error cases, the ? operator can help you pass on errors from other libraries. Otherwise panic. Because Rust's Result doesn't change control flow, you don't need to decide how callers should cope with Errors, only flag that it's their problem not yours. Because Result doesn't have a non-Error result in it if you returned an Error, your caller can't mistakenly carry on anyway, they'll just panic if they try to do that.
- AnIdiotOnTheNet 5y agoAs a seasoned Zig programmer, this is good information, though painful to read. A lot of the problems seem to be from a fundamental misunderstanding of Zig's philosophy and very basic things about how the language works. Possibly Zig needs more emphasis on those things in its documentation. I also have to wonder about a fundamental misalignment of thinking when someone says downloading and replacing a single .exe is tedious, and isn't at all excited about 'Why Zig' list. If none of those things seem like compelling ideas to you then yeah, the language probably isn't for you.
- SiVal 5y agoIf there are fundamental philosophies in a programming language (and there usually are), tutorials ought to begin with lots of tiny example programs that exemplify those fundamentals. They should continue with examples of gradually increasing complexity, but ALWAYS using those same fundamentals in various combinations to show how those fundamentals are intended to work together in all sorts of ways. If the tutorials aren't deliberately built up from the language's own defining philosophical fundamentals introduced singly and then in combination, then someone like me will just re-use the fundamentals I learned in whatever other languages I know that feel most similar to the new one. Most languages can be used in roughly parallel ways with just a change in syntax, even if that's not how they are supposed to be used, and if I (like most of us) have to get up and running ASAP in some new tool and don't have a quick on-ramp to establish the preferred patterns right from the start, I'll just have to re-use as much as I already know and just get on with it.
- llimllib 5y agoMy understanding of the Zig's team's opinion is that Zig is not currently at a place where they want to solidify the design by doing such things as writing extensive tutorials documenting how to write it - tutorials that will need to be updated every time they make breaking changes, which they explicitly still want to do. A lot of this seems to be a mismatch between people's expectations of zig's stability and the reality?
- 5y ago
- NeutralForest 5y agoI don't understand people recommending reading the source code to learn. Source codes can be really daunting because there are so many things going on. There can also be "tricks" using advanced language features, which defeats the purpose if you're trying to learn as a first timer. Also, "read the source" is, at least to me, an admission that there is no valid documentation 90% of the time. Reading the source of a tool or a small to medium library is fine, it's not fine for large projects or projects you know nothing about. Incremental examples is where it's at. You should document a fonction/type/feature/whatever with a short example with basic operations and then a more involved example with good practices and potential caveats. I know this is a lot of work, I understand it can't be like that everywhere. This is not a dig at anyone's project or language, but "read the source code" comments always strike me as unhelpful.
- iTokio 5y agoReading the source code is very helpful if you are an experimented programmer that already grasped the basics of the language. It helps you to see what are the idioms, what does optimized production code look like. Compare C++ and Go std lib and it’s easy to constate the difference in language goals that they have.
- stathibus 5y agoReading the c++ stdlib source code is never a good idea.
- FpUser 5y agoROFL. Yup. When I look at application level codes those are nice. Looking at stdlib makes me want to walk away immediately.
- einpoklum 5y agoIt's not a fun experience to be sure, but: 1. It's useful when you want to check the exact under-the-hood behavior, to figure out side-effects or corner-case semantics. 2. After a good number of years, that code becomes less scary and more icky. Some of it remains kind of scary though... 3. Reading that code teaches you a lot about considerations you may be ignoring, especially your implicit platform-dependent assumptions. 4. Nitpick: There isn't any single C++ standard library source code, it's at least 3 popular ones (GCC's libstdc++, LLVM's libc++, and Microsoft's)
- messe 5y agoI like zig, but agree with many of the points here. A couple of thoughts below, > Zig reference documentation badly needs examples. Can't figure out how to use std.fmt.parseInt. While yes, Zig documentation badly needs examples, I'm not sure this particular criticism is justified. I would have thought that the usage of parseInt, was fairly obvious from the type-signature: parseInt(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T Or, translated to C++ (and assuming the use of exceptions to return the error): template<typename T> T parseInt(const char buf[], uint8_t radix) > Need to access myArray.items[idx] instead of myArray[idx]. I get it. But very unintuitive and requires knowledge of implementation details. I'm 50:50 on this one. While it might be nice to hide the implementation here and have a .item(n: usize) member function, ArrayList explicitly manages a contiguous region of memory, so I don't see the problem with exposing it as a slice as part of the interface. > Should I pass the allocator to every function? Doesn't seem great. Maybe I'm supposed to create a global? Globals are evil and feel bad. As a rule of thumb, libraries should take allocators as arguments to their functions, while applications can either do that or create a global allocator. There is absolutely nothing wrong with using a global allocator in an application; after all it's what almost every other language does. Zig just makes that global explicit. Globals aren't always evil. > Zig's inability to infer type is annoying. If I create var count and return it and the function return type is usize then var count is obviously a usize. It's not. It could be any unsigned type of smaller size than usize.
- flohofwoe 5y ago> It's not. It could be any unsigned type of smaller size than usize. I think it refers to Rust's ability to "indirectly" infer a type by it's later usage which can be somewhere entirely else in the function (for instance when it's used as return value), while Zig has a more direct type inference (but IMHO Rust's approach definitely isn't "obviously better", because sometimes one needs to read and understand the entire code of a function to get an idea what type a variable might be).
- messe 5y agoI understood that, but my comment could have been more clear. I more meant that it's not necessarily obvious that the type check should infer that it's a `usize` just because the function returns a `usize`. It could be any type that can be implicitly cast to a `usize` which in a 64-bit Zig system is any of `u[1-64]`.
- ReleaseCandidat 5y ago> No one in the history of the world has ever been confused or upset by a + b calling a function. That depends on `a`, `b` and the function - I have seen way too many `+` that weren't commutative and/or associative. Languages which do not allow to use _other_ symbols like `⊕` too almost always lead to confusing behavior of 'usual' operators. And what does `a * b` do for vectors `a` and `b`? Is it the 'usual' 'dot product', is it component wise multiplication (`[.., ai * bi, ..]`) is it 'matrix multiplication' (`a * b^t`), is it the cross product (because the language doesn't allow to define `×` as operator), is it ... Actually, in C and C++ the implicit conversion and promotion rules are footguns enough.
- benttoothpaste 5y agoIf zig supports + for floating point addition, then it is already fine with ‘+’ being non commutative. The rest of the argument can also be applied to functions. What does product(a, b) do?
- webstrand 5y agoAside from `NaN`, how is floating point addition non-commutative? It's not associative, but `a+b` will have the same value as `b+a`.
- ReleaseCandidat 5y ago> `a+b` will have the same value as `b+a` No, as IEEE754 doesn't even guarantee that `a + b` == `a + b`. You always need to compare the absolute difference of two values against an ε. So a == b <=> |a - b| < ε But for sensible comparison of floats, the addition is commutative.
- zokier 5y ago> No, as IEEE754 doesn't even guarantee that `a + b` == `a + b` What? I'm pretty sure that is plain incorrect. Can you back that claim up with references?
- deleted 5y ago[deleted]
- smoyer 5y agoIt's interesting that the author goes through AdventOfCode hell (the code challenges are supposed to be hard, not fighting with your language), critiques Zig's raison d'etre and then predicts a bright future. To me, this seems like one of those projects that starts its marketing hype too early, gets a negative reputation and then never recovers even if the original promises have been fulfilled.
- formerly_proven 5y agoThat seems like an unfair criticism of Zig to me. The project is very clear that the language isn't stable, the compiler has bugs, the generated docs being no good and releases not being backwards compatible. The community is onboard with that. If you're doing something with Zig, you're not going to complain that for example 0.9.0 required a find'n'replace to fix every occurrence of Allocator or changed the layout of std.os.
- eterm 5y ago> [Compiling] takes about ~3 seconds minimum which is frustratingly slow I feel old, I know that any time is an opportunity to get distracted but 3 seconds doesn't strike me as a long compile time. Or is that a typo for 30, which would make more sense, which is definitely long enough to be a frustration?
- delphLonepaw 5y agoDepends on the machine, on large projects, zig can take up to 10-15 seconds in my i5 12gb, ofc, after the first hit it becomes 0.01 recompiling without change (in case you removed the out and not the cache) and the first time you compile it takes a bit (not counted) since it will precompile std (or that is what I've seen) in a global cache (just so std does not make your code take longer to compile), small things takes 1-3 seconds depending on how comptime heavy I make my code do. Zig comptime is the part that takes a bit more time compiling for being an interpreted version of the language, and the more comptime you use, the more time it takes (for a bit, even comptime heavy code can gain just a few seconds out of it, of course can gain 30 minutes, but you would be making like, weird jumps and generating strings on comptime that gets used on runtime, or an expensive algorithm that will take a long time to execute)
- ralphb 5y agoThat's funny. I am old, and I think 3 seconds is unacceptable. I use zig anyway.
- llimllib 5y agoThat complaint was weird, here's my day 7 solution in zig built in release mode: $ time zig build-exe ./a.zig -O ReleaseSmall real 0m1.728s user 0m1.544s sys 0m0.292s Which is actually slower than running the entire solution in debug mode: $ time zig run a.zig -- input.txt min 337 342641 min 470 93006301 real 0m1.138s user 0m0.789s sys 0m0.270s Totally possible that it's much slower on the dev's machine, but if he's a VR developer it seems weird that he'd have a low-powered box. [1]: https://github.com/llimllib/personal_code/blob/master/misc/advent/2021/07/a.zig https://github.com/llimllib/personal_code/blob/master/misc/a...
- stephc_int13 5y agoI disagree from this part: "I also think it's partially wrong. No one in the history of the world has ever been confused or upset by a + b calling a function." It depends. If this is simple math on vectors I think it can be OK but it should probably be a built-in feature of the language as this is common, solved and we all implement it the same way (for short vectors at least) But the + operator has been abused in the past, especially with strings concatenation and I think this is a huge liability. Such an innocent looking operator, the simplest of all operations, leading to a function call, a memory allocation and thus a very real potential memory leak, all of that hidden from the eyes... In my opinion, clarity should have priority over anything else. If an operation is computationally complex (especially with side effects) it should be at least hinted to the reader by a function call.
- tialaramex 5y agoI also would prefer not to have + as concatenate, and Linus feels strongly enough about it (and has enough influence) that Rust for the Linux kernel does not have Add and AddAssign overloaded on string types, among many concessions. However I think in general purpose programming we lost that battle when Java special-cased the operator. There's no overloading in Java, you can't have + do the Right Thing™ in Java for your 3D vector class, but it does concatenate strings because people had begun to expect that. Also, while I'm in this topic, the author argues they don't want anybody overloading either of the member access operators (which is something Python kinda-sorta supports) but notice that Rust effectively does this all the time and nobody freaks out because it feels completely natural. Your Box<Thing> isn't a Thing, so, why can you call Thing methods on it? Because it is transparently passing those into the underlying Thing by implementing the relevant overload features from core::ops and all of Rust's smart pointers work the same way. misfortunate::Double shows that this gives very strange behaviour if somebody uses it inappropriately, a Double<Thing> is actually two Things inside, but when you change it, you're changing one of them, while any immutable references are to the other one... an eerie experience.
- oldsecondhand 5y ago> However I think in general purpose programming we lost that battle when Java special-cased the operator. It was already special-cased by Pascal.
- kristoff_it 5y agoHi, I'm Loris from the ZSF. I think this is a fair post overall given what it is: a log of what it was like for one specific person to use Zig for AoC for the first time. I'll spend some words on some of the things mentioned and then add some more useful advice for forrestthewoods at the end. From my perspective the complaints mostly were about now knowing things which had varying degrees of discoverability. The first point in Day 1 about mixing optionals and error unions is an example of something very easy to discover (by reading the language reference), so I personally would consider it an unlucky mistake by the author. The second point (how to print an integer) is about something way less easily discoverable than in the previous example. You need to learn about how to print, then about `fmt` and from there the only authoritative place that contains information about format specifiers is the doc comment of `std.fmt.format`. This second example is the kind of thing that we're the weakest at communicating to people because it's not a feature of the language itself (so it doesn't belong to the language reference) and, while you could in theory find this stuff in the stdlib autogenerated docs, you still have the problem of having to discover first what to search for. This is especially problematic when Zig breaks away from what's common in programming languages. One example of that is the fact that Zig has no print statement. I've given some talks and written some articles about how to do common things and find stuff in the standard library: https://www.youtube.com/c/ZigSHOWTIME https://www.youtube.com/c/ZigSHOWTIME https://zig.news https://zig.news and, as the author metioned, Zig Learn is by far the closest thing we have to a "Zig book". https://ziglearn.org https://ziglearn.org The problem with creating content for the standard library is that it keeps changing and it's not yet the focus of development. As the language matures more content is being created (just look at how many explainers are being posted to zig.news alone) but we're not yet in a position to invest into something like a book. The author also mentioned issues with the autogenerated docs for the standard library. Those docs are currently incomplete and in fact greet you with this message as soon as you open them: These docs are experimental. Progress depends on the self-hosted compiler, consider reading the stdlib source in the meantime. I've seen some comments here about how recommending to read the source code is unhelpful. I vehemently disagree because of practicality first (if something is not documented elsewhere, then that's the best you can do) and second because reading the source code should not be considered something primitive that developers used to do before discovering fire. We all should read more and write less. That said, Andrew is going to help me start this week the work on a new doc autogeneration system based on a different design that the current (incomplete) one. I'll do most of the work while streaming on Twitch so if anybody has opinions, complaints or questions, you know where to find me: https://twitch.tv/kristoff_it https://twitch.tv/kristoff_it Going back to the post, there were also some impressions that we all fully agree with (and are working on improving), a good example being > Bit-shifting is a monumental pain in the ass. Yes, it indeed is. See: https://github.com/ziglang/zig/issues/7605 https://github.com/ziglang/zig/issues/7605 Ok, so, here's my advice to forrestthewoods if you're ever going to try Zig again: [1] Interact more with the community if you want to be pointed quickly in the right direction. You mentioned the discord server so you already interacted with people. Leverage them more: ask questions in #zig-help and don't be afraid to ask why things are a certain way. You'll probably be way less annoyed by, say, ArrayList requiring you to access its `items` field to iterate, if somebody explains to you that ArrayList is a normal userland struct defined in the stdlib and that in Zig there are no "magic methods" that the compiler picks up to do iteration etc. Or maybe you won't be less annoyed, but at least you will know if something is intentional or if it's just something that we haven't gotten around fixing yet. [2] Try to learn Zig doing something else other than Advent of Code. AoC doesn't really let Zig show it's potential because it doesn't ask you to do good software engineering. You're just supposed to write a script that gets you to the correct answer. Try instead doing a small project where you have to validate input, produce useful error messages to the user, clean up resources properly (sockets, memory, ...), etc. You will find that Zig will be much better in this second case. I'm saying this based on first hand experience btw, I've streamed the first 16 days of AoC from this year, you can find the recordings in this playlist: https://www.youtube.com/watch?v=wo580tbLSR8&list=PL5AY2Vv6EsfT4e3eBtz6qkE_KI-b-FxwO https://www.youtube.com/watch?v=wo580tbLSR8&list=PL5AY2Vv6Es... I stopped after day 16 because I felt it the exercises didn't really help me showcase any big strength of Zig, so I started working on other stuff instead.
- megumax 5y agoMy big problem with Zig is that Andrew Kelley is promising a lot of features, but doesn't really deliver much. Zig still can't proper handle UTF-8 strings [1] in 2022, which is kind of unfortunate, because it's a `requirement`. In a `recent` interview[2], he claims that Zig is faster than C and Rust, but he refers to extremely short benchmarking that has almost no value in the real world. At least Rust, as blamed and loved as it is, delivered a stable compiler and people started working on the ecosystem (in the first years, most packages were working only on nightly, but at least there were crates available). The ecosystem for zig is insignificant now and a stable release would help the language. [1] https://github.com/ziglang/zig/issues/234 https://github.com/ziglang/zig/issues/234 [2] https://about.sourcegraph.com/podcast/andrew-kelley/ https://about.sourcegraph.com/podcast/andrew-kelley/
- ralphb 5y agoRust was started in 2006. [1] Zig was started in 2015. [2] [1] https://en.wikipedia.org/wiki/Rust_(programming_language) https://en.wikipedia.org/wiki/Rust_(programming_language) [2] https://en.wikipedia.org/wiki/Zig_(programming_language) https://en.wikipedia.org/wiki/Zig_(programming_language)
- octoberfranklin 5y agoThe language called "Rust" prior to 2013 is a completely different language from what people today know as "Rust". That language had a garbage collector, mutable aliasing, and no borrow checker (the three most unique features of today's Rust), and was basically "golang with different syntax": http://smallcultfollowing.com/babysteps/blog/2012/11/18/imagine-never-hearing-the-phrase-aliasable/ http://smallcultfollowing.com/babysteps/blog/2012/11/18/imag... The whole language got rebooted shortly after the blog post above, mostly because the borrow checker made so many other things suddenly unnecessary or trivial. What we call Rust today is at most 9 years old, and any similarities to pre-2013 Rust are strictly superficial syntax. They share a name and some syntax, sort of like Java and Javascript do. Zig today at T+7 is not where Rust was in 2020 at T+7.
- 5y ago
- jmull 5y agoI also did AOC 2021 to learn zig. But I found zig interesting and useful. As someone with only 18 days more experience than OP it’s a little silly for me to say, but I think OP is getting put off the the normal learning curve of a new language and standard library. The struggle to learn different patterns faded away after not too many more days. Zig is pretty simple, and while the standard library is barely documented, it is pretty well organized and straightforward to understand. I’m pretty interested to see where zig goes, and I’m hopeful about it. (As mentioned, zig is a work-in-progress. The final form might be significantly different that what is there now,)
- jhack 5y agoI don't know how a language that sets itself up to be a better C turns into this monster of horrible syntax and patterns that's nothing like C at all.
- pphysch 5y agoHey, it's unreleased so we can project our personal vision of "ideal C" onto it. Then when those design choices get hardened into a 1.0 it will become just-another-language and we will inevitably feel let down and move into the next attempt at a Perfect Programming Language. So it goes.
- githubholobeat 5y agoI completed 10 days of Advent Of Code 2021 in Zig and loved the learning experience. Then I rewrote a snake game I made originally in Elm into Zig and compiled it to WASM. So now it runs in a browser. There is Zig standard library documentation on Zig website, but if you want to see the missing parts, go to GitHub and browse stdlib source files. Each source file contains test cases that are sufficient for understanding how it works.
- ogogmad 5y agoWould there by any interest in an imperative language with a basic Standard ML type system? Personally, while I find the imperative paradigm more intuitive than the functional paradigm, I believe functional type systems are simpler and more "right" than imperative ones are (which have been taken over by OOP). There are multi-paradigm languages, but these have large numbers of features, and complicated type systems, and I want something simple. Is there interest in this? Thanks. By SML type system, I'm thinking: - Algebraic data types - Anonymous functions - Pattern matching - Simple generics - Type inference - Simple modules and implementation hiding combined with some approach to operator overloading (which SML doesn't consider at all). And to remind you, the paradigm should be imperative, not functional. I'm thinking a use case might be in numerical linear algebra, but with support for multiple different scalar types including floats, ints, complex floats, dual numbers (for autodiff), "codual numbers" (for better autodiff), bignums, quaternions, symbolic algebra, etc. The functional paradigm is not suitable here, but Julia is perhaps overcomplicated.
- quells 5y agoI think you just described Rust (although you have to deal with / get to take advantage of the borrow checker).
- shwestrick 5y agoOCaml is pretty close already to what you're describing. The OCaml ecosystem fully embraces imperative programming. And if the syntax of OCaml is problematic, then perhaps ReasonML (https://reasonml.github.io/ https://reasonml.github.io/) is what you're looking for: a curly-braces syntax for OCaml.
- jolux 5y agoOCaml and Rust both offer these, OCaml does overloading mostly with module functors and Rust with traits. Of course they’re both multi-paradigm, OCaml is also really good for functional programming.
- Shadonototra 5y agoI don't think it is that hard to learn I'm not a professional programmer, this is all a hobby for me and i managed to pick and write an entire game with it maybe you are just bad as a programmer (nothing wrong with that) you have to learn how to learn, not everybody can do that (and it's fine) that being said, zig is a language in the making, some areas are rough, but that's to be expected
- templix 5y ago> maybe you are just bad as a programmer (nothing wrong with that) This was actually my impression after reading the post. A bad programmer making a lot of ill-informed complaints. Zig is an unfinished low-level language. Not suitable for bad programmers.
- wink 5y agoThis is a pretty bullshit argument. People can have a really easy or hard time learning a language, depending on said language. It can be too far from what you know, or too similar but just different enough. Also it's a log of personal experience and not titled "an objective criticism of Zig". I've been interested in Zig a little for a while and this was awesome to read. It hasn't made my interest any smaller or bigger, it's simply a description of one beginner's journey, and if the Zig people are smart they will analyze it and then decide which points are valid and which ones can be ignored. Neither ignoring everything nor accepting everything would probably be useful.
- socialdemocrat 5y agoZig is a very low level language. I think the fancy type system can trip up people into thinking they are working with a high-level language. Zig is basically C with a fancy type system, so you should not expect things like special String types, overloading of index based access etc. I think the author was thinking that Zig was very close to Rust or C++, when in reality it is much closer to C. I had to keep reminding myself of that many times as I was learning Zig. I had my own struggled with Zig, but not quite as much as the author. I think will probably have a much better experience if you don't try to jump and code right away but read some articles or listen to some videos to get a sense of the overall philosophy of Zig. I am normally against having to look at source code, but with Zig that is kind of needed but also not quite as bad as it sounds. Zig code base is not that large and it is relatively easy to search. You can lookup a Zig function signature very easily. You need to do this if you are going to use any of the standard library apart from the most basic stuff.
- k__ 5y agoInteresting. I had the impression Zig proponents were praising it because it wasn't as low-level as Rust.
- klyrs 5y agoQuite the opposite. The very broad-brush overview is that zig aims to replace C and rust aims to replace C++... but among other issues with that analogy, that can't happen unless zig and rust have a dead-simple integration story.
- pyrale 5y agoThe opinion that reimplementing AoC in two languages was not a good idea resonates a lot with my experience. Every year, I see people quitting their AoC run with frustration, because they don't measure their effort and try to complete each puzzle daily. The usual scenario is that, at some point, they block on a problem and spend longer than they should, or they can't solve the problem because they have some other thing to do. The next day, they try to solve that puzzle and another one, and as work piles up, they start doing it joylessly to catch up. After a while, it doesn't work anymore, because people are stressed, because they code poorly just to finish rather than taking pleasure in their craft. The strict calendar can become deadlines that remind of work woes. For a person not used to sport code, AoC can take an hour to solve, sometimes more. Over the whole advent, people can easily work for 30 to 40 hours more than they are used to. In terms of work hygiene, this is not good. Not everyone has a week's worth of work to use just before the end of year. I would advise people doing AoC to time their effort, and not hesitate to stop a puzzle and write it down for later in the year.
- kello 5y agoI definitely fall into this category. I stopped on Day 8 Part 2 this year getting frustrated and falling behind. Good practice for sure and I feel more prepared for next year, still, just wanted to post that I identify myself in what you mention here!
- deleted 5y ago[deleted]
- deleted 5y ago[deleted]
- Ostrogodsky 5y agoIf there is something I have learned over the years and I have confirmed on this thread is that if you dont like a language or the direction is taking the best course of action is to pick another one. Trying to argue with the developers is a unproductive use of your time. Another option is to wait 3-5 years and revisit the project, you will be surprised that in 90% of the cases either the project is dead on the water or the same problems still remain.
- defen 5y agoThe author is not excited about "No hidden control flow", but as a code reader it's really nice. It means that the only context you need in order to understand the control flow of a given line of code is that line itself. You don't need to check for overloaded operators, exceptions, virtual functions, etc. It's this property of Zig that I think makes "read the stdlib source" actually a viable strategy for learning how to use it.
- cbdumas 5y agoWhat did you think of the example in the article about vector math? Seems like that's an area where operator overloading actually makes the code more readable. Maybe it depends on the problem domain you're working in.
- defen 5y agoI agree that with vector math, overloaded arithmetic operators are easier to read. However, I don't see how you could add "overloading, but only for actual math" - once it's in, people will repurpose it for all kinds of cursed purposes. The implementation would probably be ugly, but I wonder if it could be implemented by using a comptime string to represent the operation, e.g. something like: fn doMath(comptime op: []const u8, args: anytype) MathReturnType(op, args) { // TODO: implement me } const result = doMath( \\a + b , .{ .a = a, .b = b } ); Where the implementation would call `.add` etc on the parameters when infix operators were used.
- Jach 5y agoThere was a comment several days ago (https://news.ycombinator.com/item?id=29825516 https://news.ycombinator.com/item?id=29825516) that made me reconsider the whole enterprise of operator overloading even for math, specifically its last paragraph. The gist is that you can easily build them to preclude useful optimizations and efficient execution, when it's often more desirable to be fast than to have syntactic sugar, hence having explicit function calls like multiply_add(a, b, c) instead of a+b*c. If you really want syntactic sugar when it comes to math, operator overloading probably isn't the way to implement it, it'd be nicer to have something with the full context so there can be optimizing reductions. Lisp macros can do that, or you might have some other kind of parser (that might have to work on strings), or with sufficient cleverness you could build an overloaded operator nest full of context-accumulating operations-to-perform that either require some doMath wrapper at the end or a final overload of operations producing a fully computed return type. I prefer languages that don't cripple expressive freedom and so overall I'm not anti-operator-overloading in general even if I think some overloads are pretty questionable (I dislike C++'s arrow overload for Optionals) but I no longer think that e.g. a math-focused library is an obvious win or exception to the downsides of the expressive power granted from operator overloading.
- meibo 5y agoAnyone though that advent of code was unusually hard this year, especially at the start? Nothing unsolvable, but I usually do these with my SO and they got stuck on the second day, wherein we usually get to day 10 or so. Felt like it picked up way sooner.
- dagmx 5y agoI think it depends on what problem spaces you're in regularly. Some people on the Reddit found it much easier than previous years, others found it much harder. It was often around the difficulty of the exponential problems, but most were okay if you're very familiar with grid data, hierarchy traversal and caching.
- jolux 5y agoTo me the biggest difference between Rust and Zig in practical terms is that Zig does not offer statically safe resource management like Rust does, and as far as I know they have no intentions of doing so in the future because they think it’s less important than all this stuff about control flow. There are a lot of interesting ideas in the language but my disagreement with them on this issue is so fundamental that I don’t feel compelled to investigate it further. Nonetheless I had gotten the impression from many posts on here before that Zig was a lot closer to finished than it really is, it seems like they’re not quite half way to something as polished as Rust 1.0. It is probably unfair to judge the language against Rust in its current state. Hopefully in a few years they will have figured out the memory safety problems and the stdlib documentation will stabilize more.
- aidenn0 5y agoYeah, I've tried out zig and it's about at the point where Rust was when there were like 5 different pointer sigils and a GC was still included.
- nyberg 5y agoIt's an open question [1] along with other safety checks both comptime and runtime [2]. The issue here is a balance of keeping language complexity low while providing safety at the same time and not depriving users of control when they know better than the type checker/lifetime analysis. [1]: https://github.com/ziglang/zig/issues/782 https://github.com/ziglang/zig/issues/782 [2]: https://github.com/ziglang/zig/issues/2301 https://github.com/ziglang/zig/issues/2301
- jolux 5y ago> not depriving users of control when they know better than the type checker/lifetime analysis. You're never deprived of control though, that's what unsafe is for, and it provides roughly the same level of safety as C or Zig would.
- kprotty 5y agounsafe Rust has its own UB pitfalls that don't exist in C nor Zig, particularly in regards to the lifetimes of references [0] and preserving provenance through experimental apis [1]. [0]: the compiler is allowed to emit extra reads/writes to references so a mut and shared one to the same memory location cant be alive at the same time, across threads, and across sometimes non-obvious scoping rules. [1]: [pointer::set_ptr_value](https://doc.rust-lang.org/std/primitive.pointer.html#method.set_ptr_value https://doc.rust-lang.org/std/primitive.pointer.html#method....) is used by containers like Arc for `from_raw` in order to carry over aliasing, mutability, and other location meta data. unsafe Rust is also less ergonomic when dealing with concepts that go against the borrow checker like Pinning for intrusive memory, ptr::read/write and ManuallyDrop for controlling unorthodox object ownership/lifetime, and MaybeUninit/NonNull for facilitating certain data layout/access optimizations. Such designs often can't be wrapped in safe-Rust without introducing runtime overhead the patterns were used to originally avoid. Languages like Zig and C however make these patterns natural or event pleasant enough to consider it over unsafe Rust.
- travisgriggs 5y ago> Some mild office competition didn't help. Does that mean that someone at his office had better luck and should be writing the success article? Or something else?
- ryukoposting 5y agoZig sounds like it's kind of in the spot Nim is in- many great ideas, most of them executed well, but a bunch of unresolved quirks that take away from the experience.
- templix 5y agoSeems like the author isn't used to low-level programming languages. Zig is a great C replacement.
- ncmncm 5y agoWhen I saw > I try to keep my C++ simple and somewhat C-like. ... I knew this would not be very informative. On one hand, the author's failure to take advantage of the power C++ offers means he will likely also fail to see how to use well what Zig does offer. (One doubts he makes any more effective use of Rust, another powerful language, than of C++.) At the same time, it should make him a better prospect to become a Zig user, not missing powerful features that could make him a more productive programmer. But he is right that "no hidden control flow" is an anti-feature. What they call "hidden control flow" seems to be what we know of as destructors (or, in Rust, the Drop trait) that is about the only piece of programming automation invented in 40 years. One might as well dispense with running water and cooked food.
- _vvhw 5y ago> But he is right that "no hidden control flow" is an anti-feature. Hmmm, "no hidden control flow" is at least what I want writing distributed systems, storage engines or device drivers. Calling it an "anti-feature" is dismissing important domains for system languages where control flow in the control plane needs to be explicitly visible, and where the necessity for things like static allocation and NASA's "The Power of 10: Rules for Developing Safety-Critical Code" mean that destructors during the lifetime of a system are an anti-pattern anyway.
- ncmncm 5y agoDestructors are a powerful tool to ensure that required events occur, an important part of safety-critical coding. Static allocation is a valuable method that is wholly compatible with use of destructors. If you imagine that freeing dynamically-allocated memory is the main use for destructors, you have utterly failed to understand them.
- _vvhw 5y agoIf I "have utterly failed to understand" destructors then I'll wear it gladly. :) I suppose we differ mostly not in our view of destructors, but in our approach to reducing dimensionality and how best to do that.
- jibalt 5y agoThere's a Hello World program at the beginning of the Zig Reference Manual. It of course shows how to print things. Many other of these failures could be avoided by reading that document, reading Zig source code in the distribution, looking at issues on Github, reading blogs and watching presentations by Andrew Kelley, etc. Zig is far from a finished project so it's premature to complain about most of the things you are complaining about.
- losvedir 5y ago> Building is slow. It takes about ~3 seconds minimum which is frustratingly slow when I'm fighting basic syntax errors. I wish there was a fast zig check. > Lack of zig-analyzer makes learning hard. > zig fmt src/main.zig is nice. Wish it automatically ran on all files. I also did (well, "am doing", can only work a bit each day and am plugging through day 7 right now) AdventOfCode in Zig this year. These points here didn't resonate with me at all. I wonder if the author knew about or tried ZLS[0]. I had it on and integrated with my VSCode and it would check a lot of things as I went and format on save. I think I followed something like this[1] to set it up. [0] https://github.com/zigtools/zls https://github.com/zigtools/zls [1] https://zig.news/jarredsumner/setting-up-visual-studio-code-for-writing-zig-kcj https://zig.news/jarredsumner/setting-up-visual-studio-code-...
- nikolay 5y agoI did improve my Go skills solving all problems in Go, learning many things the hard way, but at end my solutions were way longer and so much less ergonomic than the NumPy & Python-based I saw by the top 100 guys! I mean, come on, Go doesn't even have built-in min(), max(), abs(), and other basics (for ints)! Some of the top developers had their solutions done in a shorter time that I needed to read the overly convoluted and confusing problems. I just didn't appreciate the infantile, and wasteful pseudo-comical style of problem definitions! I've attended any programming contest back in Bulgaria and I can't recall anything that required me to read everything carefully 2-3 times before I get what needs to be done (oh, well, I wasn't 48 back then). I also got fascinated by GitHub Copilot, which actually autocompleted many of those missing basics and surprisingly "guessed" what I was trying to accomplished and offer meaningful productivity boost. I also looked into Zig, but, honestly, I didn't like many of the syntax choices made. Instead, I installed V [0], and will actually try to use it as it fits me better. [0]: https://vlang.io/ https://vlang.io/