« BackZig's Lovely Syntaxmatklad.github.ioSubmitted by Bogdanp 12 hours ago
  • n42 10 hours ago

    this is a really, really good article with a lot of nuance and a deep understanding of the tradeoffs in syntax design. unfortunately, it is evoking a lot of knee-jerk reactions from the title and emotional responses to surface level syntax aesthetics.

    the thing that stands out to me about Zig's syntax that makes it "lovely" (and I think matklad is getting at here), is there is both minimalism and consistency to the design, while ruthlessly prioritizing readability. and it's not the kind of surface level "aesthetically beautiful" readability that tickles the mind of an abstract thinker; it is brutalist in a way that leaves no room for surprise in an industrial application. it's really, really hard to balance syntax design like this, and Zig has done a lovely and respectable job at doing so.

    • scuff3d 8 hours ago

      My only complaint about the article is that it doesn't mention error handling. Lol

      Zigs use of try/catch is incredible, and by far my favorite error handling of any language. I feel like it would have fit into this article.

      • Twey 10 hours ago

        > it's not the kind of surface level "aesthetically beautiful" readability that tickles the mind of an abstract thinker

        Rather, the sort of beauty it's going for here is exactly the type of beauty that requires a bit of abstraction to appreciate: it's not that the concrete syntax is visually beautiful per se so much as that it's elegantly exposing the abstract syntax, which is inherently more regular and unambiguous than the concrete syntax. It's the same reason S-exprs won over M-exprs: consistently good often wins over special-case great because the latter imposes the mental burden of trying to fit into the special case, while the former allows you to forget that the problem ever existed. To see a language do the opposite of this, look at C++: the syntax has been designed with many, many special cases that make specific constructs nicer to write, but the cost of that is that now you have to remember all of them (and account for all of them, if templating — hence the ‘new’ uniform initialization syntax[1]).

        [1]: https://xkcd.com/927/

        This trade-off happens all the time in language design: you're looking for language that makes all the special cases nice _as a consequence of_ the general case, because _just_ being simple and consistent leads you to the Turing tarpit: you simplify the language by pushing all the complexity onto the programmer.

        • n42 9 hours ago

          I considered making the case for the parallels to Lisp, but it's not an easy case to make. Zig is profoundly not a Lisp. However, in my opinion it embodies a lot of the spirit of it. A singular syntax for programming and metaprogramming, built around an internally consistent mental model.

          I don't really know how else to put it, but it's vaguely like a C derived spiritual cousin of Lisp with structs instead of lists.

          • Twey 9 hours ago

            I think because of the forces I talked about above we experience a repeating progression step in programming languages:

            - we have a language with a particular philosophy of development

            - we discover that some concept A is awkward to express in the language

            - we add a special case to the language to make it nicer

            - someone eventually invents a new base language that natively handles concept A nicely as part of its general model

            Lisp in some sense skipped a couple of those progressions: it had a very regular language that didn't necessarily have a story for things that people at the time cared about (like static memory management, in the guise of latency). But it's still a paragon of consistency in a usable high-level language.

            I agree that it's of course not correct to say that Zig is a descendent or modern equivalent of Lisp. It's more that the virtue that Lisp embodies over all else is a universal goal of language design, just one that has to be traded off against other things, and Zig has managed to do pretty well at it.

            • bsder 4 hours ago

              > I don't really know how else to put it, but it's vaguely like a C derived spiritual cousin of Lisp with structs instead of lists.

              Zig comptime operates a lot like very old school Lisp FEXPRS before the Lisp intelligentsia booted them out because FEXPRS were theoretically messy and hard to compile.

            • pyinstallwoes 5 hours ago

              So urbit’s nock vs forth?

            • pyinstallwoes 5 hours ago

              Have any examples that stand out to you to share ?

            • bscphil 12 hours ago

              > Like Rust, Zig uses 'name' (':' Type)? syntax for ascribing types, which is better than Type 'name'

              I'm definitely an outlier on this given the direction all syntactically C-like new languages have taken, but I have the opposite preference. I find that the most common reason I go back to check a variable declaration is to determine the type of the variable, and the harder it is to visually find that, the more annoyed I'm going to be. In particular, with statically typed languages, my mental model tends to be "this is an int" rather than "this is a variable that happens to have the type 'int'".

              In Rust, in particular, this leads to some awkward syntactic verbosity, because mutable variables are declared with `let mut`, meaning that `let` is used in every declaration. In C or C++ the type would take the place of that unnecessary `let`. And even C (as of C23) will do type inference with the `auto` keyword. My tendency is to use optional type inference in places where needing to know the type isn't important to understand the code, and to specify the type when it would serve as helpful commentary when reading it back.

              • SkiFire13 11 hours ago

                > In C or C++ the type would take the place of that unnecessary `let`

                In my opinion the `let` is not so unnecessary. It clearly marks a statement that declares a variable, as opposed to other kind of statements, for example a function call.

                This is also why C++ need the "most vexing parse" ambiguity resolution.

                • reactordev 11 hours ago

                  I’m in the same boat. It’s faster mentally to grok the type of something when it comes first. The name of the thing is less important (but still important!) than the type of the thing and so I prefer types to come before names.

                  From a parser perspective, it’s easier to go name first so you can add it to the AST and pass it off to the type determiner to finish the declaration. So I get it. In typescript I believe it’s this way so parsers can just drop types all together to make it compatible with JavaScript (it’s still trivial to strip typing, though why would you?) without transpiling.

                  In go, well, you have even more crazier conventions. Uppercase vs lowercase public vs private, no inheritance, a gc that shuns away performance minded devs.

                  In the end, I just want a working std library that’s easy to use so I can build applications. I don’t care for:

                      type Add<A extends number, B extends number> = [
                        …Array<A>,
                        …Array<B>,
                      ][“length”]
                  
                  This is the kind of abuse of the type system that drives me bonkers. You don’t have to be clever, just export a function. I don’t need a type to represent every state, I need intent.
                  • MoltenMan 9 hours ago

                    I much prefer Pascal typing because it

                    1. Allows type inference without a hacky 'auto' workaround like c++ and 2. Is less ambiguous parsing wise. I.e. when you read 'MyClass x', MyClass could be a variable (in which case this is an error) or a type; it's impossible to know without context!

                    • erk__ 11 hours ago

                      Maybe it just have to do with what you are used to, it was one of the things that made me like Rust coming from F# it had the same `name : type` and `let mutable name` that I knew from there.

                      • AnimalMuppet 10 hours ago

                        I, too, go back up the code to find the type of a variable. But I see the opposite problem: if the type is first, then it becomes harder to find the line that declares the variable I'm interested in, because the variable name isn't first. It's after the type, and the type could be "int" or it could be "struct Frobnosticator". That is, the variable name is after a variable-length type, so I have to keep bouncing left to right to find the start of the variable name.

                        • Quekid5 11 hours ago

                          > I find that the most common reason I go back to check a variable declaration is to determine the type of the variable,

                          Hover the mouse cursor over it. Any reasonable editor will show the type.

                          > In Rust, in particular, this leads to some awkward syntactic verbosity, because mutable variables are declared with `let mut`, meaning that `let` is used in every declaration.

                          Rust is very verbose for strange implementation reasons... namely to avoid parse ambiguities.

                          > In C or C++ the type would take the place of that unnecessary `let`.

                          OTOH, that means you can't reliably grep for declarations of a variable/function called "foo". Also consider why some people like using

                              auto foo(int blah) -> bool
                          
                          style. This was introduced because of template nonsense (how to declare a return type before the type parameters were known), but it makes a lot of sense and makes code more greppable. Generic type parameters make putting the return type at the front very weird -- reading order wise.

                          Anyhoo...

                        • ruuda 6 hours ago

                          > as name of the type, I think I like void more than ()

                          It's the wrong name though. In type theory, (), the type with one member, is traditionally called "Unit", while "Void" is the uninhabited type. Void is the return type of e.g. abort.

                          • ivanjermakov 6 hours ago

                            If I understood `abort` semantics correctly, it has a type of `never` or Rust's `!`. Which has a meaning "unobtainable value because control flow went somwhere else". `void` is closer to `unit` or `()` because it's the type with no allowed values.

                            Cool trick: some languages (e.g. TypeScript) allow `void` generics making parameters of that type optional.

                            • steveklabnik 5 hours ago

                              Unit has exactly one allowed value.

                              • ivanjermakov 4 hours ago

                                Right, but there is not much semantic difference in having one or having zero allowed values.

                                • NobodyNada 4 hours ago

                                  There's a huge semantic difference: a type with zero allowed values can never be constructed.

                                  This means that a function that returns an uninhabited type is statically known to not return -- since there's no way it could construct an uninhabited value for a return expression. It also means you can coerce a value of uninhabited type into any other type, because any code which recieves a value of an uninhabited type must be unreachable.

                                  For instance, in Rust you can write the following:

                                      let weekday_name: &str = match weekday_number {
                                          1 => "Sunday",
                                          2 => "Monday",
                                          3 => "Tuesday",
                                          4 => "Wednesday",
                                          5 => "Thursday",
                                          6 => "Friday",
                                          7 => "Saturday",
                                          i => panic!("invalid weekday number: {i});
                                      };
                                  
                                  Because panic! aborts the program and doesn't return, its return type is uninhabited. Thus, a panic! can be used in a context where a string is expected.
                                  • steveklabnik 2 hours ago

                                    One vs zero is an incredibly fundamental difference.

                              • cornstalks 6 hours ago

                                It fulfills the same role as C and C++'s void type. I don't think most systems programmers care about type theorist bikeshedding about purity with formal theory.

                                A ton of people coming to Zig are going to be coming from C and C++. void is fine.

                                • bmacho 6 hours ago

                                  [deleted]

                                  • steveklabnik 5 hours ago

                                    It doesn’t work because you’re trying to pass unit to sayhi, which doesn’t take any arguments.

                                • z_open 11 hours ago

                                  > Raw or multiline strings are spelled like this:

                                      const still_raw =
                                          \\const raw =
                                          \\    \\Roses are red
                                          \\    \\  Violets are blue,
                                          \\    \\Sugar is sweet
                                          \\    \\  And so are you.
                                          \\    \\
                                          \\;
                                          \\
                                      ;
                                  
                                  This syntax seems fairly insane to me.
                                  • IshKebab 10 hours ago

                                    Maybe if you've never tried formatting a traditional multiline string (e.g. in Python, C++ or Rust) before.

                                    If it isn't obvious, the problem is that you can't indent them properly because the indentation becomes part of the string itself.

                                    Some languages have magical "removed the indent" modes for strings (e.g. YAML) but they generally suck and just add confusion. This syntax is quite clear (at least with respect to indentation; not sure about the trailing newline - where does the string end exactly?).

                                    • qzzi 2 hours ago

                                      C and Python automatically concatenate string literals, and Rust has the concat! macro. There's no problem just writing it in a way that works correctly with any indentation. No need for weird-strings.

                                        " one\n"
                                        "  two\n"
                                        "   three\n"
                                      • konart 9 hours ago

                                        I may be missing something but come Go has a simple:

                                            `A
                                               simple
                                                  formatted
                                                     string
                                            `
                                        
                                        ?
                                        • rybosome 9 hours ago

                                          Yours is rendered as:

                                          A\n\tsimple\n\t\tformatted\n\t\t\tstring\n\t

                                          If you wanted it without the additional indentation, you’d need to use a function to strip that out. Typescript has dedent which goes in front of the template string, for example. I guess in Zig that’s not necessary which is nice.

                                          • xigoi 6 hours ago

                                            The problem is that usually you have something like

                                              fn main() {
                                                if something {
                                                  print(`A
                                              simple
                                              formatted
                                              string`)
                                                }
                                              }
                                            
                                            which looks ugly and confusing.
                                          • norir 8 hours ago

                                            Significant whitespace is not difficult to add to a language and, for me, is vastly superior than what zig does both for strings and the unnecessary semicolon that zig imposes by _not_ using significant whitespace.

                                            I would so much rather read and write:

                                                let x = """
                                                  a
                                                  multiline string
                                                  example
                                                """
                                            
                                            than

                                                let x =
                                                  //a
                                                  //multiline string
                                                  //example
                                                ;
                                            
                                            In this particular example, zig doesn't look that bad, but for longer strings, I find adding the // prefix onerous and makes moving strings around different contexts needlessly painful. Yes, I can automatically add them with vim commands, but I would just rather not have them at all. The trailing """ is also unnecessary in this case, but it is nice to have clear bookends. Zig by contrast lacks an opening bracket but requires a closing bracket, but the bracket it uses `;` is ambiguous in the language. If all I can see is the last line, I cannot tell that a string precedes it, whereas in my example, you can.

                                            Here is a simple way to implement the former case: require tabs for indentation. Parse with recursive descent where the signature is

                                                (source: string, index: number, indent: number, env: comp_env) => ast
                                            
                                            Multiline string parsing becomes a matter of bumping the indent parameter. Whenever the parser encounters a newline character, it checks the indentation and either skips it, or if is less than the current indentation requires a closing """ on the next line at a reduced indentation of one line.

                                            This can be implemented in under 200 lines of pure lua with no standard library functions except string.byte and string.sub.

                                            It is common to hear complaints about languages that have syntactically significant whitespace. I think a lot of the complaints are fair when the language does not have strict formatting rules: python and scala come to mind as examples that do badly with this. With scala, practically everyone ends up using scalafmt which slows down their build considerably because the language is way too permissive in what it allows. Yaml is another great example of significant whitespace done poorly because it is too permissive. When done strictly, I find that a language with significant whitespace will always be more compact and thus, in my opinion, more readable than one that does not use it.

                                            I would never use zig directly because I do not like its syntax even if many people do. If I was mandated to use it, I would spend an afternoon writing a transpiler that would probably be 2-10x faster than the zig compiler for the same program so the overhead of avoiding their decisions I disagree with are negligible.

                                            Of course from this perspective, zig offers me no value. There is nothing I can do with zig that I can't do with c so I'd prefer it as a target language. Most code does not need to be optimized, but for the small amount that does, transpiling to c gives me access to almost everything I need in llvm. If there is something I can't get from c out of llvm (which seems highly unlikely), I can transpile to llvm instead.

                                            • winwang 7 hours ago

                                              Does `scalafmt` really slow down builds "considerably"? I find that difficult to believe, relative to compile time.

                                            • z_open 10 hours ago

                                              Even if we ignore solutions other languages have come up with, it's even worse that they landed on // for the syntax given that it's apparently used the same way for real comments.

                                              • WCSTombs 9 hours ago

                                                But those are two different slashes? \\ for strings and // for comments?

                                                • IshKebab 10 hours ago

                                                  Yeah I agree \\ is not the best choice visually (and because it looks quite similar to //

                                                  I would have probably gone with ` or something.

                                                  • n42 10 hours ago

                                                    > it's even worse that they landed on // for the syntax

                                                    .. it is using \\

                                                    • hinkley 10 hours ago

                                                      I worked with browsers since before most people knew what a browser was and it will never cease to amaze me how often people confuse slash and backslash, / and \

                                                      It’s some sort of mental glitch that a number of people fall into and I have absolutely no idea why.

                                                      • Mawr 5 hours ago

                                                        I doubt those very people would confuse the two when presented with both next to each other: / \ / \. The issue is, they're not characters used day-to-day so few people have made the association that the slash is the one going this way / and not the one going the other way \. They may not even be aware that both exist, and just pick the first slash-like symbol they see on their keyboards without looking further.

                                                        • Twey 9 hours ago

                                                          I wonder if it's dyslexia-adjacent. Dyslexic people famously have particular difficulty distinguishing rotated and reflected letterforms.

                                                          • hinkley 7 hours ago

                                                            Could be. The frequency is such that it could be dyslexics. It's not all the time, but it's a steady rate of incidence.

                                                            • quesera 6 hours ago

                                                              I think in the 90's it was just people repeating a pattern they learned from Windows/DOS.

                                                              It used to grate on my nerves to hear people say, e.g. "H T T P colon backslash backslash yahoo dot com".

                                                              But I think they always typed forward slash, like they knew the correct slash to use based on the context, but somehow always spoke it in DOSish.

                                                    • Blackarea 10 hours ago

                                                      We can just use a crate for that and don't have to have this horrible comment like style that brings its own category of problems. https://docs.rs/indoc/latest/indoc/

                                                      • Twey 9 hours ago

                                                        And what if you do want to include two spaces at the beginning of the block (but not any of the rest of the indentation)?

                                                        Choice of specific line-start marker aside, I think this is the best solution to the indented-string problem I've seen so far.

                                                        • gf000 7 hours ago

                                                          I think Java's solution is much cleaner.

                                                          • IshKebab 6 hours ago

                                                            For those of us that haven't used Java for a decade...

                                                            > In text blocks, the leftmost non-whitespace character on any of the lines or the leftmost closing delimiter defines where meaningful white space begins.

                                                            From https://blogs.oracle.com/javamagazine/post/text-blocks-come-...

                                                            It's not a bad option but it does mean you can't have text where every line is indented. This isn't uncommon - e.g. think about code generation of a function body.

                                                            • Twey 2 hours ago

                                                              Right, this is a pretty common syntax, but doesn't address the same problem as Zig's syntax.

                                                              I've only seen two that do: the Zig approach, and a postprocessing ‘dedent’ step.

                                                    • n42 10 hours ago

                                                      Zig does not really try to appeal to window shoppers. this is one of those controversial decisions that, once you become comfortable with the language by using it, you learn to appreciate.

                                                      spoken as someone who found the syntax offensive when I first learned it.

                                                      • conaclos 5 hours ago

                                                        I like the idea of repeating the delimiter on every line. However `//` looks like a comment to me. I could simply choose double quote:

                                                            const still_raw =
                                                                "const raw =
                                                                "    "Roses are red
                                                                "    "  Violets are blue,
                                                                "    "Sugar is sweet
                                                                "    "  And so are you.
                                                                "    "
                                                                ";
                                                                "
                                                            ;
                                                        
                                                        This cannot be confused with a string literal because a string literal cannot contain newline feeds.
                                                        • flexagoon 3 hours ago

                                                          What if you have something like

                                                              const raw =
                                                                  "He said "Hello"
                                                                  "to me
                                                              ;
                                                          
                                                          Wouldn't that be a mess to parse? How would you know that "He said " is not a string literal and that you have to continue parsing it as a multiline string? How would you distinguish an unclosed string literal from a multiline string?
                                                        • whitehexagon 8 hours ago

                                                          I think Kotlin solves it quite nicely with the trimIndent. I seem to recall Golang was my fav, and Java my least, although I think Java also finally added support for a clean text block.

                                                          Makes cut 'n' paste embedded shader code, assembly, javascript so much easier to add, and more readable imo. For something like a regular expressions I really liked Golang's back tick 'raw string' syntax.

                                                          In Zig I find myself doing an @embedFile to avoid the '\\' pollution.

                                                          • ivanjermakov 6 hours ago

                                                            It is not the insane syntax, but quite insane problem to solve.

                                                            Usually, representing multiline strings within another multiline string requires lots of non-trivial escaping. This is what this example is about: no escaping and no indent nursery needed in Zig.

                                                            • seabombs 3 hours ago

                                                              I think the syntax highlighting for this could make it more readable. Make the leading `\\` a different color to the string content.

                                                              • hardwaregeek 10 hours ago

                                                                My immediate thought was hmm, that's weird but pretty nice. The indentation problem indeed sucks and with a halfway decent syntax highlighter you can probably de-emphasize the `//` and make it less visually cluttered.

                                                                • rybosome 9 hours ago

                                                                  Visually I dislike the \\, but I see this solves the problem of multiline literals and indentation in a handy, unambiguous way. I’m not actually aware of any other language which solves this problem without a function.

                                                                  • throw10920 10 hours ago

                                                                    It seems very reasonable and comes with several technical and cognitive advantages. I think you're just having a knee-jerk emotional reaction because it's different than what you're used to, not because it's actually bad.

                                                                    • conorbergin 10 hours ago

                                                                      I think everyone has this reaction until they start using it, then it makes perfect sense, especially when using editors that have multiple cursors and can operate on selections.

                                                                      • klas_segeljakt 10 hours ago

                                                                        When I first read it I thought it was line comments.

                                                                        • watersb 5 hours ago

                                                                          Upvoting because similar comments here suggest that you are not alone.

                                                                          People are having trouble distinguishing between '//' and '\\'.

                                                                        • steveklabnik 10 hours ago

                                                                          I had the exact opposite reaction.

                                                                          • fcoury 11 hours ago

                                                                            I really like zig but that syntax is indeed insane.

                                                                          • kcartlidge 11 hours ago

                                                                            I much prefer C# 11's raw string literals. It takes the indentation of the first line and assumes the subsequent ones have the same indentation.

                                                                              string json = $"""
                                                                                  <h1>{title}</h1>
                                                                                  <article>
                                                                                      Welcome to {sitename}.
                                                                                  </article>
                                                                                  """;
                                                                            
                                                                            And it even allows for using embedded curly braces as real characters:

                                                                              string json = $$"""
                                                                                  <h1>{{title}}</h1>
                                                                                  <article>
                                                                                      Welcome to {{sitename}}, which uses the <code>{sitename}</code> syntax.
                                                                                  </article>
                                                                                  """;
                                                                            
                                                                            The $ (meaning to interpolate curly braces) appears twice, which switches interpolation to two curly braces, leaving the single ones untouched.
                                                                            • Metasyntactic 10 hours ago

                                                                              Just a minor correction (as I'm the author of c#'s raw string literal feature).

                                                                              The indentation of the final ` """` line is what is removed from all other lines. Not the indentation of the first line. This allows the first line to be indented as well.

                                                                              Cheers, and I'm glad you like it. I thought we did a really good job with that feature :-)

                                                                              • gf000 8 hours ago

                                                                                Really not trying to go into any of the "holy wars" here, but could you please compare C#'s feature to Java's multi-line strings? I'm only familiar with the latter, and I would like to know if they are similar in concept or not.

                                                                                • winwang 7 hours ago

                                                                                  That's a fantastic design idea, and it seems to require all the other lines to have the same indentation "prefix".

                                                                                  Haven't used much C#, but I love Scala's `.stripPrefix` and `StringContext`.

                                                                              • phplovesong 12 hours ago

                                                                                I find Zig syntax noicy. I dont like the @TypeOf (at symbol) and pals, and the weird .{.x} syntax feels off.

                                                                                Zig has some nice things going on but somehow code is really hard to read, admitting its a skill issue as im not that versed in zig.

                                                                                • dsego 6 hours ago

                                                                                  Zig is noisy and and the syntax is really not elegant. One reason I like odin's syntax, it's minimal and so well thought out.

                                                                                  • flohofwoe 12 hours ago

                                                                                    The dot is just a placeholder for an inferred type, and IMHO that makes a lot of sense. E.g. you can either write this:

                                                                                        const p = Point{ .x = 123, .y = 234 };
                                                                                    
                                                                                    ...or this:

                                                                                        const p: Point = .{ .x = 123, .y = 234 };
                                                                                    
                                                                                    When calling a function which expects a Point you can omit the verbose type:

                                                                                        takePoint(.{ .x = 123, .y = 234 });
                                                                                    
                                                                                    In Rust I need to explicitly write the type:

                                                                                        takePoint(Point{ x: 123, y: 234);
                                                                                    
                                                                                    ...and in nested struct initializations the inferred form is very handy, e.g. Rust requires you to write this (not sure if I got the syntax right):

                                                                                        const x = Rect{
                                                                                            top_left: Point{ x: 123, y: 234 },
                                                                                            bottom_right: Point{ x: 456, y: 456 },
                                                                                        };
                                                                                    
                                                                                    ...but the compiler already knows that Rect consists of two nested Points, so what's the point of requiring the user to type that out? So in Zig it's just:

                                                                                        const x = Rect{
                                                                                            .top_left = .{ .x = 123, .y = 234 },
                                                                                            .bottom_right = .{ .x = 456, .y = 456 },
                                                                                        };
                                                                                    
                                                                                    Requiring the explicit type on everything can get noisy really fast in Rust.

                                                                                    Of course the question is whether the leading dot in '.{' could be omitted, and personally I would be in favour of that. Apparently it simplifies the parser, but such implementation details should get in the way of convenience IMHO.

                                                                                    And then there's `.x = 123` vs `x: 123`. The Zig form is copied from C99, the Rust form from Javascript. Since I write both a lot of C99 and Typescript I don't either form (and both Zig and Rust are not even close to the flexibility and convenience of the C99 designated initialization syntax unfortunately).

                                                                                    Edit: fixed the Rust struct init syntax.

                                                                                    • hinkley 10 hours ago

                                                                                      Thanks for the explanation, but I don’t think you’ve sold me on .x

                                                                                      Think I’d rather do the Point{} syntax.

                                                                                      • do_not_redeem 12 hours ago

                                                                                        Zig is planning to get rid of explicit `T{}` syntax, in favor of only supporting inferred types.

                                                                                        https://github.com/ziglang/zig/issues/5038

                                                                                        So the explanation of a dot standing in for a type doesn't make sense in the long run.

                                                                                        • dminik 6 hours ago

                                                                                          Ahh, a perfect example of why Zig is uninteresting to me:

                                                                                          https://github.com/ziglang/zig/issues/5038#issuecomment-2441...

                                                                                          A language hostile to LSP/intellisense.

                                                                                          • surajrmal an hour ago

                                                                                            How is it hostile? The lsp can do type inference just the same as the compiler.

                                                                                          • flohofwoe 11 hours ago

                                                                                            Ah, I wasn't aware of that proposal. But yeah in that case I would also heavily prefer to "drop the dot" :)

                                                                                            IMHO Odin got it exactly right. For a variable with explicit type:

                                                                                               a_variable : type = val;
                                                                                            
                                                                                            ...or for inferred type:

                                                                                               a_variable := val;
                                                                                            
                                                                                            ...and the same for constants:

                                                                                               a_const : type : val;
                                                                                               a_const :: val;
                                                                                            
                                                                                            ...but I think that doesn't fit into Zig's parser design philosophy (e.g. requiring some sort of keyword upfront so that the parser knows the context it's in right from the start instead of delaying that decision to a later time).
                                                                                          • tialaramex 6 hours ago

                                                                                            Because we've said x is a constant we're obliged to specify its type. For variables we're allowed to use inference and in most cases the type can be correctly inferred, but for constants or function signatures inference is deliberately prohibited.

                                                                                                const x: Rect = ....
                                                                                            
                                                                                            [Note that in Zig what you've written isn't a constant, Zig takes the same attitude as C and C++ of using const to indicate an immutable rather than a constant]
                                                                                        • nromiun 11 hours ago

                                                                                          I like Zig as well, but I won't call its syntax lovely. Go shows you can do pretty well without ; for line breaks, without : for variable types etc.

                                                                                          But sure, if you only compare it with Rust, it is a big improvement.

                                                                                          • nine_k 11 hours ago

                                                                                            I personally find Go's bare syntax harder to parse when reading, and I spend more time reading code than typing it (even while writing).

                                                                                            An excessively terse syntax becomes very unforgiving, when a typo is not noticed by the compiler / language server, but results in another syntactically correct but unexpected program, or registers as a cryptic error much farther downstream. Cases in point: CoffeeScript, J.

                                                                                            • nromiun 11 hours ago

                                                                                              That is why syntax debates are so difficult. There is no objectively best syntax. So we are all stuck with subjective experience. For me I find Python (non-typed) and Golang syntax easiest to read.

                                                                                              Too many symbols like ., :, @, ; etc just mess with my brain.

                                                                                              • aatd86 7 hours ago

                                                                                                Yes it's sigils that are the culprits more often than not. They are often semantically irrelevant and just make things easier to parse for the machines. Happy Go doesn't indulge too much in them.

                                                                                            • IshKebab 10 hours ago

                                                                                              Removing stuff doesn't necessarily make the syntax better, otherwise we'd all use Lisp and the space bar wouldn't exist: https://en.wikipedia.org/wiki/Scriptio_continua

                                                                                              • nromiun 10 hours ago

                                                                                                True. But my point is that adding stuff doesn't necessarily make the syntax better either. Otherwise we would all be using Perl by now. The sweet spot is somewhere in the middle.

                                                                                            • pton_xd 12 hours ago

                                                                                              "Zig doesn’t have lambdas"

                                                                                              This surprises me (as a C++ guy). I use lambdas everywhere. What's the standard way of say defining a comparator when sorting an array in Zig?

                                                                                              • tapirl 11 hours ago

                                                                                                Normal function declarations.

                                                                                                This is indeed a point which makes Zig inflexible.

                                                                                                • lenkite 10 hours ago

                                                                                                  By adopting a syntax like

                                                                                                      fn add(x: i32, i32) i32
                                                                                                  
                                                                                                  
                                                                                                  they have said perma-goodbye to lambdas. They should have at-least considered

                                                                                                      fn add(x: i32, i32): i32
                                                                                                  • bitwizeshift 5 hours ago

                                                                                                    Why “perma goodbye”?

                                                                                                    Go has a similar function declaration, and it supports anonymous functions/lambdas.

                                                                                                    E.g. in go, an anonymous func like this could be defined as

                                                                                                    foo := func(x int, _ int) int { … }

                                                                                                    So I’d imagine in Zig it should be feasible to do something like

                                                                                                    var foo = fn(x: i32, i32) i32 { … }

                                                                                                    unless I’m missing something?

                                                                                                    • lenkite 4 hours ago

                                                                                                      Anonymous functions aren't the same as lambda functions. People in the Go community keep asking for lambda functions and never get them. There should be no need for func/fn and explicit return. Because the arrow would break stuff is one of the reasons.

                                                                                                      See

                                                                                                      https://github.com/golang/go/issues/59122

                                                                                                      https://github.com/golang/go/issues/21498

                                                                                                          res := is.Map(func(i int)int{return i+1}).Filter(func(i int) bool { return i % 2 == 0 }).
                                                                                                                   Reduce(func(a, b int) int { return a + b })
                                                                                                      
                                                                                                      
                                                                                                      vs

                                                                                                          res := is.Map((i) => i+1).Filter((i)=>i % 2 == 0).Reduce((a,b)=>a+b)
                                                                                                    • hinkley 10 hours ago

                                                                                                      They could still fix it with arrow functions, but it’s always gonna look weird.

                                                                                                      Some other people have tried to explain how they prefer types before variable declarations, and they’ve done a decent job of it, but it’s the function return type being buried that bothers me the most. Since I read method signatures far more often than method bodies.

                                                                                                      fn i32 add(…) is always going to scan better to me.

                                                                                                      • lenkite 9 hours ago

                                                                                                        OK, but with generics return type first tends to becomes a monster.

                                                                                                  • jmull 11 hours ago

                                                                                                    You can declare an anonymous struct that has a function and reference that function inline (if you want).

                                                                                                    There's a little more syntax than a dedicated language feature, but not a lot more.

                                                                                                    What's "missing" in zig that lambda implementations normally have is capturing. In zig that's typically accomplished with a context parameter, again typically a struct.

                                                                                                    • veber-alex 10 hours ago

                                                                                                      So basically, Zig doesn't have lambdas, but because you still need lambdas, you need to reinvent the wheel each time you need it?

                                                                                                      Why don't they just add lambdas?

                                                                                                      • flipgimble 10 hours ago

                                                                                                        Because to use lambdas you're asking the language to make implicit heap allocations for captured variables. Zig has a policy that all allocation and control flow are explicit and visible in the code, which you call re-inventing the wheel.

                                                                                                        Lambdas are great for convenience and productivity. Eventually they can lead to memory cycles and leaks. The side-effect is that software starts to consume gigabytes of memory and many seconds for a task that should take a tiny fraction of that. Then developers either call someone who understands memory management and profiling, or their competition writes a better version of that software that is unimaginably faster. ex. https://filepilot.tech/

                                                                                                        • do_not_redeem 10 hours ago

                                                                                                          Nothing about lambdas requires heap allocation. See also: C++, Rust

                                                                                                          • porridgeraisin 9 hours ago

                                                                                                            If the lambda captures some value, and also outlives the current scope, then that captured value has to necessarily be heap allocated.

                                                                                                            • noselasd 8 hours ago

                                                                                                              No, (in C++) the lambda can capture the the variable by value, and the lambda itself can be passed around by value. If you capture a variable by reference or pointer that your lambda outlives, your code got a serious bug.

                                                                                                              • gf000 7 hours ago

                                                                                                                And in Rust, it will enforce correct usage via the borrow checker - the outlive case simply will not compile.

                                                                                                                If you do want it, you have the option to, say, heap allocate.

                                                                                                              • do_not_redeem 9 hours ago

                                                                                                                That would be a bug, so just... don't do that?

                                                                                                                If you return a pointer to a local variable that outlives the scope, the pointer would be dangling. Does that mean we should ban pointers?

                                                                                                                If you close over a pointer to a local variable that outlives the scope, the closure would be dangling. Does that mean we should ban closures?

                                                                                                          • jmull 9 hours ago

                                                                                                            > So basically...

                                                                                                            Well, not really.

                                                                                                            Consider lambdas in C++ (that was the perspective of the post I replied to). Before lambdas, you used functors to do the same thing. However, the syntax was slightly cumbersome and C++ has the design philosophy to add specialized features to optimize specialized cases, so they added lambdas, essentially as syntactic sugar over functors.

                                                                                                            In zig the syntax to use an anonymous struct like a functor and/or lambda is pretty simple and the language has the philosophy to keep the language small.

                                                                                                            Thus, no need for lambdas. There's no re-inventing anything, just using the language as it designed to be used.

                                                                                                        • tux1968 11 hours ago

                                                                                                          Same as C, define a named function, and pass a pointer to the sorting function.

                                                                                                          • flohofwoe 11 hours ago

                                                                                                            Unlike C you can stamp out a specialized and typesafe sort function via generics though:

                                                                                                            https://ziglang.org/documentation/master/std/#std.sort.binar...

                                                                                                            • tsimionescu 11 hours ago

                                                                                                              And what if you need to close over some local variable?

                                                                                                              • flohofwoe 11 hours ago

                                                                                                                Not possible, you'll need to pass the captured variables explicitly into the 'lambda' via some sort of context parameter.

                                                                                                                And considering the memory management magic that would need to be implemented by the compiler for 'painless capture' that's probably a good thing (e.g. there would almost certainly be a hidden heap allocation required which is a big no-no in Zig).

                                                                                                                • pton_xd 11 hours ago

                                                                                                                  As far as I know lambdas in C++ will not heap allocate. Basically equivalent to manually defining a struct, with all your captures, and then stack allocating it. However if you assign a lambda to a std::function, and it's large enough, then you may get a heap allocation.

                                                                                                                  Making all allocations explicit is one thing I do really like about Zig.

                                                                                                                  • andrepd 10 hours ago

                                                                                                                    Why would capturing require a heap allocation? Neither Rust nor C++ do this.

                                                                                                                    • flohofwoe 10 hours ago

                                                                                                                      You need to store the captured data somewhere if the lambda is called after the outer function returns. AFAIK C++ (or rather std::function) will heap-allocate if the capture size goes above some arbitrary limit (similar to small-string optimizations in std::string). Not sure how Rust handles this case, probably through some "I can't let you do that, Dave" restrictions ;)

                                                                                                                      • steveklabnik 10 hours ago

                                                                                                                        The trick is that “if”. Rust won’t ever automatically heap allocate, but if your closure does get returned to a place where the capture would be dangling, it will fail to compile. You can then choose to heap allocate it if you wish, or do something else instead.

                                                                                                                        Heap allocating them is fairly rare, because most usages are in things like combinators, which have no reason to enlarge their scope like that.

                                                                                                                        • andrepd 9 hours ago

                                                                                                                          But that's not a closure, a closure/lambda is not an std::function. It's its own type, basically syntactic sugar for a struct with the captures vars and operator().

                                                                                                                          Of course if you want to store it on a type-erased container like std::function then you may need to heap allocate. Rust's equivalent would be a Box<dyn Fn>.

                                                                                                                      • nine_k 11 hours ago

                                                                                                                        ...or escape analysis and lifetimes, but we already have Rust %)

                                                                                                                • ww520 10 hours ago

                                                                                                                  Zig is great. I have fun writing in it. But there’re a few things bug me.

                                                                                                                  - Difficult to return a value from a block. Rust treats the value of the last expression of a block as the return value of the block. Have to jump through hoops in Zig to do it with label.

                                                                                                                  - Unable to chain optional checks, e.g. a?.b?.c. Support for monadic types would be great so general chaining operations are supported.

                                                                                                                  - Lack of lambda support. Function blocks are already supported in a number of places, i.e. the for-loop block and the catch block.

                                                                                                                  • the__alchemist 11 hours ago

                                                                                                                    I wish Zig had lovely vector, quaternion, matrix etx syntax. The team's refusal to add operator overloading will prevent this.

                                                                                                                    • flohofwoe 11 hours ago

                                                                                                                      You don't need operator overloading for vector and matrix math, see pretty much all GPU languages. What Zig is missing is a complete mapping of the Clang Extended Vector and Matrix extensions (instead of the quite limited `@Vector` type):

                                                                                                                      https://clang.llvm.org/docs/LanguageExtensions.html#vectors-...

                                                                                                                      https://clang.llvm.org/docs/LanguageExtensions.html#matrix-t...

                                                                                                                      • renox 7 hours ago

                                                                                                                        Agreed, their reason for not allowing it is weird. No hidden overloading? OK make it explicit then: #+, #/ would be fine.

                                                                                                                        • Cyph0n 6 hours ago

                                                                                                                          That would open a can of worms, because then the next thing would use a different symbol. AFAIK, Scala had a huge issue with random symbols polluting code readability.

                                                                                                                      • hardwaregeek 12 hours ago

                                                                                                                        Everyone agrees that "syntax doesn't matter", but implicit in that is "syntax doesn't matter, so let's do what I prefer". So really, syntax does matter. Personally I prefer the Rust/Zig/Go syntax of vaguely C inspired with some nice fixes, as detailed in the post. Judging by the general success of that style, I do wonder if more functional languages should consider an alternative syntax in that style. The Haskell/OCaml concatenative currying style with whitespace is elegant, but sufficiently unfamiliar that I do think it hurts adoption.

                                                                                                                        After all, Rust's big success is hiding the spinach of functional programming in the brownie of a systems programming language. Why not imitate that?

                                                                                                                        • nromiun 11 hours ago

                                                                                                                          That saying never made any sense to me either. After all syntax is your main interface to a language. Anything you do has to go through the syntax.

                                                                                                                          Some people say the syntax just kind of disappears for them after some time. That never seems to happen with me. When I am reading any code the syntax gets even more highlighted.

                                                                                                                          • tmtvl 7 hours ago

                                                                                                                            One of the reasons why I use Lisp is because the syntax helps me keep my mind organised. C-style syntax is just too chaotic for me to handle.

                                                                                                                            • hinkley 10 hours ago

                                                                                                                              People who do a lot of deep work on code, particularly debugging and rearchitecting, tend to have opinions about syntax and code style that are more exacting. As one of those people I’ve always felt that the people working the code the hardest deserve to have an outsized vote on how to organize the code.

                                                                                                                              Would that potentially reduce throughput a bit? Probably. But here’s the thing: most management notice how the bad situations go more than the happiest path. They will kick you when you’re down. So tuning the project a bit for the stressful days is like Safety. Sure it would be great to put a shelf here but that’s where the fire extinguishers need to go. And sure it would be great not interrupting the workday for fire safety drills, but you won’t be around to complain about it if we don’t have them. It’s one of those counterintuitive things, like mise en place is for a lot of people. Discipline is a bit more work now for less stress later.

                                                                                                                              You get more predictable throughput from a system by making the fastest things a little slower to make the slow things a lot faster. And management needs predictability.

                                                                                                                              • brabel 10 hours ago

                                                                                                                                I always thought that languages (or at least editors) should allow the user to use whatever syntax they want! It's possible. Just look at Kotlin and Java... you can write the exact same code in both, even if you discount the actual differences in the languages. It's not hard to see how you could use Haskell, or Python syntax, to write the exact same thing. People don't like the idea too much because they think that makes it sharing code and reading code together harder, but I don't buy that because at least I myself, read code by myself 99% of the time, and the few times I read code with someone else, I can imagine we can just agree quite easily on which syntax to use - or just agree to always use the "default" syntax to avoid disagreements.

                                                                                                                                I dream to one day get the time to write an editor plugin that lets you read and write in any syntax... then commit the code in the "standard" syntax. How hard would that be?!

                                                                                                                              • brabel 10 hours ago

                                                                                                                                If you like C-like syntax and want a functional language that uses it, try Gleam: https://gleam.run/

                                                                                                                                Quite lovely looking code.

                                                                                                                                    fn spawn_greeter(i: Int) {
                                                                                                                                      process.spawn(fn() {
                                                                                                                                        let n = int.to_string(i)
                                                                                                                                        io.println("Hello from " <> n)
                                                                                                                                      })
                                                                                                                                    }
                                                                                                                                
                                                                                                                                There's also Reason, which is basically OCaml (also compiles to JS - funnily enough, Gleam does that too.. but the default is the Erlang VM) with C-like syntax: https://reasonml.github.io/
                                                                                                                              • Twey 11 hours ago

                                                                                                                                > I think Kotlin nails it: val, var, fun. Note all three are monosyllable, unlike const and fn!

                                                                                                                                At least in my pronunciation, all five of those are monosyllabic! (/kQnst/, /f@n/).

                                                                                                                                (Nice to see someone agree with me on minimizing syllable count, though — I definitely find it easier to chunk[1] fewer syllables.)

                                                                                                                                [1]: https://en.m.wikipedia.org/wiki/Chunking_(psychology)

                                                                                                                                The use of Ruby block parameters for loops in a language without first-class blocks/lambdas is a bit weird to me. I might have preferred a syntax that looks more like normal variable binding.

                                                                                                                                • culebron21 9 hours ago

                                                                                                                                  I've been seeing articles on Zig for the last 2 years, and was interested, but it seems the language community is too far from my area of interest -- data and geospatial, and the tools in my sphere in Zig aren't mature enough. E.g. to parse a CSV, you have to use very simple packages or just use a tokenizer and parse the tokens yourself.

                                                                                                                                  • ivanjermakov 6 hours ago

                                                                                                                                    > Almost always there is an up-front bound for the number of iterations until the break, and its worth asserting this bound, because debugging crashes is easier than debugging hangs.

                                                                                                                                    Perhaps in database systems domain yes, but in everything else unconditional loop is meant to loop indefinitely. Think event loops, web servers, dynamic length iterations. And in many cases `while` loop reads nicer when it has a break condition instead of a condition variable defined outside of the loop.

                                                                                                                                    • MrResearcher 12 hours ago

                                                                                                                                      It's still not clear to me how you can make two comptime closures with different contents and pass those as a functor into the same function. It needs to have a sort of VTable to invoke the function, and yet since the contents are different, the objects are different, and their deallocation will be different too. Defining VTable in zig seems to be a pretty laborious endeavor, with each piece sewn manually.

                                                                                                                                      • zoogeny 10 hours ago

                                                                                                                                        There was a recent Zig podcast where Andrew Kelley explicitly states that manually defining a VTable is their solution to runtime polymorphism [1]. In general this means wrapping your data in a struct, which is reasonable for almost anything other than base value types.

                                                                                                                                        1. https://youtu.be/x3hOiOcbgeA?si=Kb7SrhdammEiVvDN&t=7620

                                                                                                                                      • losvedir 11 hours ago

                                                                                                                                        > Note all three are monosyllable, unlike const and fn!

                                                                                                                                        I'm not sure if I'm parsing this right, but is the implication that "const" is not monosyllabic? It certainly is for me. How else do people say it? I get "fn" because people might say "eff enn" but I don't see what the equivalent for const would be.

                                                                                                                                        • IshKebab 11 hours ago

                                                                                                                                          > As Zig has only line-comments

                                                                                                                                          Such a good decision. There's no reason to use block comments in 2025.

                                                                                                                                          • zabzonk 12 hours ago

                                                                                                                                            In my experience, everyone finds the syntax of their favourite language lovely - I love (mostly) C++.

                                                                                                                                            • aeonik 11 hours ago

                                                                                                                                              Not me!

                                                                                                                                              I don't like the syntax of Lisps, with the leading parenthesis to begin every expression.

                                                                                                                                              I use it anyway because it's so useful and powerful. And I don't have any better ideas.

                                                                                                                                              • zabzonk 10 hours ago

                                                                                                                                                > leading parenthesis to begin every expression

                                                                                                                                                well, that's not really what it is doing - it is saying apply this function to these parameters.

                                                                                                                                                • brabel 10 hours ago

                                                                                                                                                  No, it's an s-expression... it may be a function call, but it may also be a macro, and inside a macro it may be a parameter list or a number of other things.

                                                                                                                                              • ben-schaaf 12 hours ago

                                                                                                                                                As someone who works almost exclusively in C++, the whole "most vexing parse" makes the syntax indefensible.

                                                                                                                                                • zabzonk 11 hours ago

                                                                                                                                                  In my experience, people simply go "Doh, of course!" or don't write the wrong code in the first place. It has never caused me any real problems.

                                                                                                                                                  For those of you that may be curious, or not know C++, in C++ this:

                                                                                                                                                      Obj x();
                                                                                                                                                  
                                                                                                                                                  is a declaration of a function called x that returns an Obj. Whereas this:

                                                                                                                                                      Obj x;
                                                                                                                                                  
                                                                                                                                                  defines (possibly, depending on context) an instance of the type Obj called x.

                                                                                                                                                  Most people get over this pretty quickly.

                                                                                                                                                  If that is your main complaint about a language then the language doesn't have too many problems. Not that I'm suggesting that C++ doesn't have more serious problems.

                                                                                                                                                • guidopallemans 11 hours ago

                                                                                                                                                  I don't like how Python does lambdas, its indentation-based blocks, how there's both ' and ", I could go on.

                                                                                                                                                  • jeltz 12 hours ago

                                                                                                                                                    I am an exception then. Rust may be my favourite language but the syntax is pretty awful and one of its biggest weaknesses. I also love Ruby but I am pretty meh about its syntax.

                                                                                                                                                    • zabzonk 12 hours ago

                                                                                                                                                      I don't think many people would start to use a language unless the found the syntax at least a little simpatico - but I guess they could be drawn by the semantics it provides.

                                                                                                                                                      • sampullman 11 hours ago

                                                                                                                                                        I (almost) don't consider syntax at all when considering a language, just whether it's suitable for the task.

                                                                                                                                                        The exception is languages with syntax that require a non-standard keyboard.

                                                                                                                                                        • jeltz 9 hours ago

                                                                                                                                                          I mostly chose languages due to semantics.

                                                                                                                                                    • melodyogonna 10 hours ago

                                                                                                                                                      Very interesting, Zig seems really nice. There was a severe lack of resources when I tried to get into it few years ago, it is nice to see that situation improving in real time.

                                                                                                                                                      The part about integer literal is similar to Mojo, with a comp-time type that has to be materialized to another type at runtime. In Mojo though this can be done implicitly so you don't need explicit casting.

                                                                                                                                                      • Y_Y 11 hours ago

                                                                                                                                                        > C uses a needlessly confusing spiral rule

                                                                                                                                                        Libellous! The "spiral rule" for C is an abomination and not part of the language. I happen to find C's type syntax a bit too clever for beginners, but it's marvellously consistent and straightforward. I can read types fine without that spiral nonsense, even if a couple of judicious typedefs are generally a good idea for any fancy function types.

                                                                                                                                                        • Twey 11 hours ago

                                                                                                                                                          > fancy function types

                                                                                                                                                          If the syntax were straightforward, plain old function types wouldn't be ‘fancy’ :)

                                                                                                                                                          • Y_Y 9 hours ago

                                                                                                                                                            Ambiguous parse. I don't mean that all function types are fancy, I mean that some are fancy (when you have a function pointers in the arguments or return value, or several layers of indirection), and that those which are fancy are helped by typedefs.

                                                                                                                                                            • Twey 8 hours ago

                                                                                                                                                              I maintain — they shouldn't be fancy. ‘Fancy’ is the mark of something your language doesn't support well. They can be _long_, and that's often worth breaking up, but if something is ‘fancy’ it's because it's not clear to read, so you should attach some identifier to it that explains what it's supposed to mean. That's significantly, though not exclusively, a function (ha) of the syntax used to express the concept.

                                                                                                                                                              If you're working with Roman numerals, division is a very fancy thing to do.

                                                                                                                                                              • Y_Y 8 hours ago

                                                                                                                                                                Fair enough. I think it qualifies as "essential complexity" and in my limited experience it's not a common use case and so doesn't make sense to optimize for.

                                                                                                                                                                In fact in my academic and professional career most of the highly "functional" C that I've come across has been written by me, when I'd rather amuse myself than make something readable.

                                                                                                                                                                [0] https://en.wikipedia.org/wiki/Essential_complexity

                                                                                                                                                                • Twey 2 hours ago

                                                                                                                                                                  It (this particular example, of function pointer syntax) is absolutely just incidental complexity, though. E.G. Haskell

                                                                                                                                                                  (a -> b) -> c -> d

                                                                                                                                                                  becomes C

                                                                                                                                                                  D (*f(B (*)(A)))(C)

                                                                                                                                                                  and it's no surprise that the former is considered much less fancy than the latter.

                                                                                                                                                                  Of course it's not common — because the language makes it painful :) The causation is the other way around. We've seen in plenty of languages that if first-class functions are ergonomic to use then people use them all over the place.

                                                                                                                                                                  • Y_Y an hour ago

                                                                                                                                                                    Without curring and closures it certainly will be more painful!

                                                                                                                                                                    I might write the equivalent signature,

                                                                                                                                                                      D f(A, B, C)
                                                                                                                                                                    
                                                                                                                                                                    and then reorganize things to just pass f around, or make a struct if you really want to bake in your first function.
                                                                                                                                                          • throwawaymaths 11 hours ago

                                                                                                                                                            It's not context free though.

                                                                                                                                                            • Rusky 10 hours ago

                                                                                                                                                              It is context free, just ambiguous.

                                                                                                                                                          • qcnguy 11 hours ago

                                                                                                                                                            Weird the author finds it lovely and compares to Kotlin, but doesn't find Kotlin superior. Kotlin invested heavily in a really nice curly brace syntax. It is actually the nicest out there. In every point the author makes, it feels like Kotlin did it the same or better. For example:

                                                                                                                                                            1. Integer literals. "var a = 1" doesn't work, seems absurd. In Kotlin literals do have strong types, but coercion is allowed when defining variables so "var a = 1" works, and "var a: Long = 1" works even though you can write a literal long as 1L. This means you can write numbers to function parameters naturally.

                                                                                                                                                            2. Multi-line string literals. OK this is a neat idea, but what about copy/paste? In Kotlin you can just write """ .. """.trimIndent() and then copy paste some arbitrary text into the string, the indent will be removed for you. The IDE will also help with this by adding | characters which looks more natural than \\ and can be removed using .trimMargin(), only downside is the trimming is done at runtime but that could easily be fixed without changing the language.

                                                                                                                                                            3. Record literals. This syntax is called lovely because it's designed for grep, a properly funded language like Kotlin just uses named kwargs to constructors which is more natural. There's no need to design the syntax for grep because Kotlin is intended to be used with a good IDE that can answer this query instantly and precisely.

                                                                                                                                                            4. Function syntax. "fn foo(a: i32) i32 {}" seems weird. If the thing that has a type and the type are normally separated by a : then why not here? Kotlin does "fun foo(a: Int): Int {}" which is more consistent.

                                                                                                                                                            5. Locals. Agree with author that Kotlin nails it.

                                                                                                                                                            6. Not using && or ||, ok this one Zig wins, the Zig way is more consistent and reads better. Kotlin does have `and` and `or` as infix operator functions, but they are for the bitwise operations :(

                                                                                                                                                            7. Explicit returns. Kotlin supports blocks that return values and also doesn't need semicolons, so not quite sure what the tradeoff here is supposed to be about.

                                                                                                                                                            8. Loops being expressions is kinda cool but the Kotlin equivalent of his example is much easier to read still: "val thing = collection.first { it.foo > bar }". It compiles to a for loop due to the function inlining.

                                                                                                                                                            9. Generics. Zig's way seems primitive and unnecessarily complex. In Kotlin it is common to let the compiler infer generics based on all available information, so you can just write "someMethod(emptyList())" and emptyList<T>() infers to the correct type based on what someMethod expects.

                                                                                                                                                            Overall Zig looks like a lot of modern languages, where they are started as a hobby or side project of some guy and so the language is designed around whatever makes implementing the compiler most convenient. Kotlin is unusual because it doesn't do that. It was well funded from the start, so the syntax is designed first and foremost to be as English-like and convenient as possible without leaving the basic realm of ordinary curly-brace functions-and-oop style languages. It manages to be highly expressive and convenient without the syntax feeling overly complex or hard to learn.

                                                                                                                                                            • lenkite 10 hours ago

                                                                                                                                                              > There's no need to design the syntax for grep because Kotlin is intended to be used with a good IDE that can answer this query instantly and precisely.

                                                                                                                                                              On large projects its still cheaper and faster to grep from the CLI than to use Intellij IDE search. Esp if you wish to restrict search to subsets of dirs.

                                                                                                                                                              Command-line greppability is a serious use case.

                                                                                                                                                              Zig is also 5-10x faster to compile than Kotlin.

                                                                                                                                                              On function syntax, I agree with you. It was a mistake not to use fn:ReturnType. It has destroyed future lambdas.

                                                                                                                                                              • brabel 10 hours ago

                                                                                                                                                                > On large projects its still cheaper and faster to grep from the CLI than to use Intellij IDE search. Esp if you wish to restrict search to subsets of dirs.

                                                                                                                                                                You must never have used Intellij to say that... it hurts me to hear this. If I catch a developer "grepping" for some type in the CLI, I will sit down with them for a few hours explaining how to use an IDE and how grep is just dumb text search without any of the semantic understanding of the code that an IDE has, and they should never do that again.

                                                                                                                                                                EDIT: IntelliJ is better than grep even at "free text" search... Much better as it will show you the results as you type, extremely fast, and lets you preview them and see their "surrounding code"... and you can then choose to navigate to a place instantly... and yes, you can scope the search to a particular directory if you want... if you can't see how this is miles superior to CLI grep, then there's no use arguing as you've made up your mind you just love being in the CLI for no actual rational reason.

                                                                                                                                                                • lenkite 9 hours ago

                                                                                                                                                                  Umm..I _am_ talking about the free text search. Miles superior is NOT miles faster when you want speed. You need not change tabs, fiddle laboriously with the finicky scope drop-down and create custom scopes. Instead you execute an ripgrep command with filter directories (or load alias/from history), pipe to neovim and get auto-preview of results, including surrounding preview code. You don't have to load a huge project and wait for looong code-indexing.

                                                                                                                                                                  if you can't see how this is miles superior to IDE grep (esp when exploring a number of large projects), then there's no use arguing as you've made up your mind you just love being in the IDE for no actual rational reason.

                                                                                                                                                                  • brabel 9 hours ago

                                                                                                                                                                    > Instead you execute an ripgrep command with filter directories (or load alias/from history), pipe to neovim

                                                                                                                                                                    Talk about moving the goal posts.

                                                                                                                                                                    • lenkite 9 hours ago

                                                                                                                                                                      > Talk about moving the goal posts.

                                                                                                                                                                      Hey, you are the one who moved the goal posts fist by bringing up use-cases like surrounding preview. You can use ripgrep -> fzf -> bat in one single command if you want to stick to the CLI for fast search previews. But I personally like neovim's comfort better and it is extremely fast to load compared to Intellij. (Not to mention, you can keep feeding the quickfix list more search results from other places)

                                                                                                                                                                  • johnisgood 6 hours ago

                                                                                                                                                                    He said cheaper and faster. It takes 5-10 minutes for IntelliJ to start up properly for me, and doing anything in it is just too slow. (rip)grep is way faster.

                                                                                                                                                                    Yes yes, I need a better PC. (rip)grep would still be faster, however, but I would use the IDE.

                                                                                                                                                                • johnisgood 10 hours ago

                                                                                                                                                                  It is not just syntax that matters. For one I dislike JVM-based languages. I still write it sometimes for work.

                                                                                                                                                                  • qcnguy 9 hours ago

                                                                                                                                                                    There is also Kotlin/Native, but yes, I am only talking about syntax here.

                                                                                                                                                                • pjmlp 11 hours ago

                                                                                                                                                                  Having @ and .{ } all over the place is hardly lovely, as is having modules like JavaScript's CJS.

                                                                                                                                                                  • minitech 2 hours ago

                                                                                                                                                                    There’s nothing wrong with the CommonJS approach except that it’s not designed for static analysis (and whether that was really an issue is debatable). In Zig, it’s compile-time.

                                                                                                                                                                    • Akronymus 11 hours ago

                                                                                                                                                                      It's especially bad on a qwertz keyboard as well.

                                                                                                                                                                      • flohofwoe 11 hours ago

                                                                                                                                                                        That's why "real programmers" use English keyboard layout regardless of the physical keyboard ;)

                                                                                                                                                                        Curly brace syntax would never have been invented in Europe (case in point: Python and Pascal).

                                                                                                                                                                        • Akronymus 11 hours ago

                                                                                                                                                                          Oh for sure. I am using qwerty myself. And my fav language (f#) has relatively few curly braces.

                                                                                                                                                                          • christophilus 11 hours ago

                                                                                                                                                                            F# is wonderful. I wish someone would make an F# that compiled as fast as OCaml or Go and which had Go’s standard library and simple tooling.

                                                                                                                                                                            • pjmlp 9 hours ago

                                                                                                                                                                              .NET ecosystem is only matched by Java, and Native AOT exists, even if there are some issues with printf, due to its current implementation.

                                                                                                                                                                          • pjmlp 11 hours ago

                                                                                                                                                                            Real programmers deliver business value, regardless of what keyboard they have at their disposal.

                                                                                                                                                                            Just like great musicians make the difference in the band, regardless of the instruments scattered around the studio.

                                                                                                                                                                            • lenkite 10 hours ago

                                                                                                                                                                              JSON is everywhere nowadays - How can one complain about curly braces {} ? EU programmers would have already mapped {} after their first REST API.

                                                                                                                                                                            • hinkley 10 hours ago

                                                                                                                                                                              I’ve been using Dvorak for 28 years and I still fat finger punctuation daily.

                                                                                                                                                                            • fallow64 10 hours ago

                                                                                                                                                                              Every file is an implicit struct, so importing a module is just importing a struct with static members.

                                                                                                                                                                              You can also do something like:

                                                                                                                                                                              ```Point.zig x: i32, y: i32,

                                                                                                                                                                              const Self = @This(); fn add(self: Self, other: Self) Self { // ... } ```

                                                                                                                                                                              • pjmlp 9 hours ago

                                                                                                                                                                                Yeah, which is basically how requires() kind of works.

                                                                                                                                                                            • lvl155 11 hours ago

                                                                                                                                                                              Zig is just fun to write. And to me, it’s actually what I wish Rust was like. Rust is a great language, and no one’s going to argue that point but writing Zig for the first time was so refreshing. That said, Rust is now basically default for systems and Zig came too late.

                                                                                                                                                                              • throwawaymaths 10 hours ago

                                                                                                                                                                                never underestimate second mover advantage. if zig gets static borrow checking, it would be amazing

                                                                                                                                                                                • gf000 6 hours ago

                                                                                                                                                                                  I really doubt a borrow checker could fit with zig's design goals, and you can't just add it after the fact.

                                                                                                                                                                              • yegle 10 hours ago

                                                                                                                                                                                > As Zig has only line-comments, this means that \n is always whitespace.

                                                                                                                                                                                Do I read this correctly that it replaces `\n` at the end of the line with a whitespace? CJK users probably won't be happy with the additional whitespaces.

                                                                                                                                                                                • throwawaymaths 10 hours ago

                                                                                                                                                                                  that's not correct.

                                                                                                                                                                                • MrBuddyCasino 8 hours ago

                                                                                                                                                                                  > I think Kotlin nails it: val, var, fun.

                                                                                                                                                                                  Been saying this, and I find it strange how few agree.

                                                                                                                                                                                  • WalterBright 11 hours ago

                                                                                                                                                                                        const x: i32 = 92;
                                                                                                                                                                                    
                                                                                                                                                                                    D has less syntax:

                                                                                                                                                                                        const int x = 92;
                                                                                                                                                                                    
                                                                                                                                                                                    Just for fun, read each declaration out loud.
                                                                                                                                                                                    • IshKebab 11 hours ago

                                                                                                                                                                                      "less syntax" isn't necessarily better. The goal isn't to have as few characters as possible.

                                                                                                                                                                                      • johnisgood 10 hours ago

                                                                                                                                                                                        True, it is subjective. I prefer C, Go, PHP, ... and OCaml, Erlang, Elixir, Perl, and sometimes Ada and Common Lisp.

                                                                                                                                                                                        I like the syntaxes of each, although Ada is too verbose to me, and with Factor and Common Lisp I have a skill issue.

                                                                                                                                                                                        • WalterBright 10 hours ago

                                                                                                                                                                                          I agree. I'm not a fan of minimized syntax (see Haskell). But I am a fan of easy to read syntax!

                                                                                                                                                                                          • tmtvl 7 hours ago

                                                                                                                                                                                            If it were we would all be using APL.

                                                                                                                                                                                          • WalterBright 11 hours ago

                                                                                                                                                                                            Zig:

                                                                                                                                                                                                fn ArrayListType(comptime T: type) type {
                                                                                                                                                                                            
                                                                                                                                                                                            D:

                                                                                                                                                                                                T ArrayListType(T)() {
                                                                                                                                                                                            • dpassens 10 hours ago

                                                                                                                                                                                              I don't know D but shouldn't that be

                                                                                                                                                                                                  struct ArrayListType(T) {
                                                                                                                                                                                              
                                                                                                                                                                                              ?
                                                                                                                                                                                              • brabel 10 hours ago

                                                                                                                                                                                                Walter is showing the equivalent function declaration... you will eventually create a generic type as you say, but in the Zig example, that was a function, not a struct.

                                                                                                                                                                                                • dpassens 10 hours ago

                                                                                                                                                                                                  If my understanding of D's template syntax is correct, then Walter is showing a the declaration of a function called ArrayListType which is generic over T and returns a T. The original Zig code returns the struct type itself, so it is functionally equivalent to my example, provided I understood how D templates work.

                                                                                                                                                                                                  • brabel 9 hours ago

                                                                                                                                                                                                    The Zig code returns any `type`, it's impossible to say what that is without looking at the implementation. It can be different types completely depending on the comptime arguments.

                                                                                                                                                                                                    But I agree it probably returns a struct type.

                                                                                                                                                                                                    Assuming that's the case, you're right and the equivalent would be:

                                                                                                                                                                                                    Zig:

                                                                                                                                                                                                        fn ArrayListType(comptime T: type) type {
                                                                                                                                                                                                    
                                                                                                                                                                                                    D:

                                                                                                                                                                                                        ArrayList!T ArrayListType(T)() {
                                                                                                                                                                                                    
                                                                                                                                                                                                    But now the D version is more specific about what it returns, so it's still not exactly equivalent.
                                                                                                                                                                                                    • dpassens 6 hours ago

                                                                                                                                                                                                      No, you misunderstand. The function doesn't return any type, it returns _a_ type. Types are values in Zig and returning them from function is how generics are implemented.

                                                                                                                                                                                              • do_not_redeem 9 hours ago

                                                                                                                                                                                                Why is omitting the fact that T is a type useful? T could be a normal value too.

                                                                                                                                                                                                This reminds me of C in the 1970s where the compiler assumed every typo was a new variable of type int. Explicit is good.

                                                                                                                                                                                                • WalterBright 8 hours ago

                                                                                                                                                                                                  > Why is omitting the fact that T is a type useful?

                                                                                                                                                                                                  It's the default, because most templates are templated on types. If you want a constant int as part of the template type,

                                                                                                                                                                                                      ArrayListType(int I)
                                                                                                                                                                                                  
                                                                                                                                                                                                  > This reminds me of C in the 1970s where the compiler assumed every typo was a new variable of type int

                                                                                                                                                                                                  I think you're referring to function declarations without prototypes. D's syntax does not suffer from that issue.

                                                                                                                                                                                                  BTW,

                                                                                                                                                                                                      T func(T)(T x) { return x + 1; }
                                                                                                                                                                                                  
                                                                                                                                                                                                  is a declaration of a "function template". The first parameter list consists of the template parameters, and are compile time types and constants. The second parameter list is the conventional function parameter list. If the first parameter list is omitted, then it's a function.
                                                                                                                                                                                              • WalterBright 11 hours ago

                                                                                                                                                                                                Zig:

                                                                                                                                                                                                    const std = @import("std");
                                                                                                                                                                                                
                                                                                                                                                                                                D:

                                                                                                                                                                                                    import std;
                                                                                                                                                                                                • brabel 10 hours ago

                                                                                                                                                                                                  I think this is not equivalent because in D, this imports all symbols in the package `std` while in Zig, you just get a "struct" called `std`. I think the equivalent D is:

                                                                                                                                                                                                      import std=std;
                                                                                                                                                                                                  • WalterBright 43 minutes ago

                                                                                                                                                                                                    What I wrote is equivalent to the zig declaration. Google says:

                                                                                                                                                                                                        const my_module = @import("my_module.zig");
                                                                                                                                                                                                    
                                                                                                                                                                                                        This allows you to access pub (public) declarations from my_module.zig through the my_module identifier.
                                                                                                                                                                                              • Western0 11 hours ago

                                                                                                                                                                                                Love Ruby and Cristal syntax ;-)

                                                                                                                                                                                                • darthrupert 11 hours ago

                                                                                                                                                                                                  Absolutely. Kinda of the best of many worlds, because it has what looks like an indentation-based syntax but actually blocks are delimited by start and end keywords. This makes it possible (unlike, say, python) to programmatically derive correct code formatting more reliably.

                                                                                                                                                                                                  And no pointless semicolons.

                                                                                                                                                                                                • do_not_redeem 12 hours ago

                                                                                                                                                                                                  Since we're talking syntax... it's mildly infuriating that the zig parser is not smart enough to understand expressions like `const x=a()orelse b();`. You have to manually add a space before `orelse` -- but isn't that what `zig fmt` is for? I have RSI and it's maddening having to mash the arrow keys and add/remove whitespace until the parser is happy.

                                                                                                                                                                                                  I've heard the argument that people might confuse binary operators for prefix/postfix operators, but I don't buy it. Who would think an operator named `orelse` is anything but binary?

                                                                                                                                                                                                  • hmry 12 hours ago

                                                                                                                                                                                                    "Read the file, or else!" The threatening postfix operator

                                                                                                                                                                                                    • hiccuphippo 9 hours ago

                                                                                                                                                                                                      No idea why it wouldn't add the space, but you could configure your editor to always add a space after `)` and let zig fmt remove the space when not needed.

                                                                                                                                                                                                      • nateglims 11 hours ago

                                                                                                                                                                                                        The error when you accidentally pass a variable directly instead of in a .{} is also really unclear.

                                                                                                                                                                                                      • norir 8 hours ago

                                                                                                                                                                                                        From my vantage point, Zig's syntax perfectly matches the language: it is ad-hoc, whimsical and serendipitous. It is lacking in grace, elegance and compassion.