[HN Gopher] Zig's Lovely Syntax
       ___________________________________________________________________
        
       Zig's Lovely Syntax
        
       Author : Bogdanp
       Score  : 184 points
       Date   : 2025-08-10 15:33 UTC (7 hours ago)
        
 (HTM) web link (matklad.github.io)
 (TXT) w3m dump (matklad.github.io)
        
       | do_not_redeem wrote:
       | 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 wrote:
         | "Read the file, or else!" The threatening postfix operator
        
         | nateglims wrote:
         | The error when you accidentally pass a variable directly
         | instead of in a .{} is also really unclear.
        
         | hiccuphippo wrote:
         | 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.
        
       | phplovesong wrote:
       | 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.
        
         | flohofwoe wrote:
         | 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.
        
           | do_not_redeem wrote:
           | 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.
        
             | flohofwoe wrote:
             | 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).
        
             | dminik wrote:
             | Ahh, a perfect example of why Zig is uninteresting to me:
             | 
             | https://github.com/ziglang/zig/issues/5038#issuecomment-244
             | 1...
             | 
             | A language hostile to LSP/intellisense.
        
           | hinkley wrote:
           | Thanks for the explanation, but I don't think you've sold me
           | on .x
           | 
           | Think I'd rather do the Point{} syntax.
        
           | tialaramex wrote:
           | 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]
        
         | dsego wrote:
         | 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.
        
       | zabzonk wrote:
       | In my experience, everyone finds the syntax of their favourite
       | language lovely - I love (mostly) C++.
        
         | jeltz wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
             | I mostly chose languages due to semantics.
        
         | ben-schaaf wrote:
         | As someone who works almost exclusively in C++, the whole "most
         | vexing parse" makes the syntax indefensible.
        
           | zabzonk wrote:
           | 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.
        
         | aeonik wrote:
         | 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 wrote:
           | > 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 wrote:
             | 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.
        
         | guidopallemans wrote:
         | I don't like how Python does lambdas, its indentation-based
         | blocks, how there's both ' and ", I could go on.
        
       | MrResearcher wrote:
       | 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 wrote:
         | 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
        
       | pton_xd wrote:
       | "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?
        
         | tux1968 wrote:
         | Same as C, define a named function, and pass a pointer to the
         | sorting function.
        
           | tsimionescu wrote:
           | And what if you need to close over some local variable?
        
             | flohofwoe wrote:
             | 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).
        
               | nine_k wrote:
               | ...or escape analysis and lifetimes, but we already have
               | Rust %)
        
               | pton_xd wrote:
               | 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 wrote:
               | Why would capturing require a heap allocation? Neither
               | Rust nor C++ do this.
        
               | flohofwoe wrote:
               | 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 wrote:
               | 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 wrote:
               | 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>.
        
           | flohofwoe wrote:
           | Unlike C you can stamp out a specialized and typesafe sort
           | function via generics though:
           | 
           | https://ziglang.org/documentation/master/std/#std.sort.binar.
           | ..
        
         | tapirl wrote:
         | Normal function declarations.
         | 
         | This is indeed a point which makes Zig inflexible.
        
           | lenkite wrote:
           | 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
        
             | hinkley wrote:
             | 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 wrote:
               | OK, but with generics return type first tends to becomes
               | a monster.
        
         | jmull wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
               | Nothing about lambdas requires heap allocation. See also:
               | C++, Rust
        
               | porridgeraisin wrote:
               | If the lambda captures some value, and also outlives the
               | current scope, then that captured value has to
               | necessarily be heap allocated.
        
               | do_not_redeem wrote:
               | 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?
        
               | noselasd wrote:
               | 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 wrote:
               | 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.
        
             | jmull wrote:
             | > 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.
        
       | hardwaregeek wrote:
       | 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 wrote:
         | 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.
        
           | brabel wrote:
           | 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?!
        
           | hinkley wrote:
           | 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.
        
           | tmtvl wrote:
           | 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.
        
         | brabel wrote:
         | 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/
        
       | bscphil wrote:
       | > 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 wrote:
         | > 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 wrote:
         | 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_.
        
         | erk__ wrote:
         | 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.
        
         | Quekid5 wrote:
         | > 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...
        
         | AnimalMuppet wrote:
         | 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.
        
         | MoltenMan wrote:
         | 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!
        
       | Y_Y wrote:
       | > 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.
        
         | throwawaymaths wrote:
         | It's not context free though.
        
           | Rusky wrote:
           | It is context free, just ambiguous.
        
         | Twey wrote:
         | > fancy function types
         | 
         | If the syntax were straightforward, plain old function types
         | wouldn't be 'fancy' :)
        
           | Y_Y wrote:
           | 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 wrote:
             | 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 wrote:
               | 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
        
       | pjmlp wrote:
       | Having @ and .{ } all over the place is hardly lovely, as is
       | having modules like JavaScript's CJS.
        
         | Akronymus wrote:
         | It's especially bad on a qwertz keyboard as well.
        
           | flohofwoe wrote:
           | 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 wrote:
             | Oh for sure. I am using qwerty myself. And my fav language
             | (f#) has relatively few curly braces.
        
               | christophilus wrote:
               | 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 wrote:
               | .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 wrote:
             | 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 wrote:
             | JSON is everywhere nowadays - How can one complain about
             | curly braces {} ? EU programmers would have already mapped
             | {} after their first REST API.
        
           | hinkley wrote:
           | I've been using Dvorak for 28 years and I still fat finger
           | punctuation daily.
        
         | fallow64 wrote:
         | 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 wrote:
           | Yeah, which is basically how _requires()_ kind of works.
        
       | the__alchemist wrote:
       | I wish Zig had lovely vector, quaternion, matrix etx syntax. The
       | team's refusal to add operator overloading will prevent this.
        
         | flohofwoe wrote:
         | 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 wrote:
         | Agreed, their reason for not allowing it is weird. No hidden
         | overloading? OK make it explicit then: #+, #/ would be fine.
        
           | Cyph0n wrote:
           | 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.
        
       | nromiun wrote:
       | 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 wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
         | 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 wrote:
           | 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.
        
       | Western0 wrote:
       | Love Ruby and Cristal syntax ;-)
        
         | darthrupert wrote:
         | 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.
        
       | WalterBright wrote:
       | const x: i32 = 92;
       | 
       | D has less syntax:                   const int x = 92;
       | 
       | Just for fun, read each declaration out loud.
        
         | WalterBright wrote:
         | Zig:                   const std = @import("std");
         | 
         | D:                   import std;
        
           | brabel wrote:
           | 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 wrote:
         | Zig:                   fn ArrayListType(comptime T: type) type
         | {
         | 
         | D:                   T ArrayListType(T)() {
        
           | dpassens wrote:
           | I don't know D but shouldn't that be                   struct
           | ArrayListType(T) {          ?
        
             | brabel wrote:
             | 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 wrote:
               | 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 wrote:
               | 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 wrote:
               | 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 wrote:
           | 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 wrote:
             | > 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.
        
         | IshKebab wrote:
         | "less syntax" isn't necessarily better. The goal isn't to have
         | as few characters as possible.
        
           | WalterBright wrote:
           | I agree. I'm not a fan of minimized syntax (see Haskell). But
           | I am a fan of easy to read syntax!
        
           | johnisgood wrote:
           | 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.
        
           | tmtvl wrote:
           | If it were we would all be using APL.
        
       | lvl155 wrote:
       | 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 wrote:
         | never underestimate second mover advantage. if zig gets static
         | borrow checking, it would be amazing
        
           | gf000 wrote:
           | I really doubt a borrow checker could fit with zig's design
           | goals, and you can't just add it after the fact.
        
       | Twey wrote:
       | > 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.
        
       | losvedir wrote:
       | > 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.
        
       | z_open wrote:
       | > 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.
        
         | fcoury wrote:
         | I really like zig but that syntax is indeed insane.
        
         | IshKebab wrote:
         | 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?).
        
           | z_open wrote:
           | 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.
        
             | n42 wrote:
             | > it's even worse that they landed on // for the syntax
             | 
             | .. it is using \\\
        
               | hinkley wrote:
               | 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.
        
               | Twey wrote:
               | I wonder if it's dyslexia-adjacent. Dyslexic people
               | famously have particular difficulty distinguishing
               | rotated and reflected letterforms.
        
               | hinkley wrote:
               | 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 wrote:
               | 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.
        
               | Mawr wrote:
               | 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.
        
             | IshKebab wrote:
             | Yeah I agree \\\ is not the best choice visually (and
             | because it looks quite similar to //
             | 
             | I would have probably gone with ` or something.
        
             | WCSTombs wrote:
             | But those are two different slashes? \\\ for strings and //
             | for comments?
        
           | Blackarea wrote:
           | 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 wrote:
             | 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 wrote:
               | I think Java's solution is much cleaner.
        
               | IshKebab wrote:
               | 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.
        
           | konart wrote:
           | I may be missing something but come Go has a simple:
           | `A            simple               formatted
           | string         `          ?
        
             | rybosome wrote:
             | 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.
        
               | bmacho wrote:
               | Maybe Go doesn't get right the whitespaces so it is a bad
               | example, but they are possible to get right. Zig values
               | the ability to parse the code parallel more than the
               | loveliness of the syntax (or the readability of the code)
               | and it shows.
        
             | xigoi wrote:
             | The problem is that usually you have something like
             | fn main() {         if something {           print(`A
             | simple       formatted       string`)         }       }
             | 
             | which looks ugly and confusing.
        
           | norir wrote:
           | 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 wrote:
             | Does `scalafmt` really slow down builds "considerably"? I
             | find that difficult to believe, relative to compile time.
        
         | hardwaregeek wrote:
         | 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.
        
         | n42 wrote:
         | 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.
        
         | throw10920 wrote:
         | 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.
        
         | steveklabnik wrote:
         | I had the exact opposite reaction.
        
         | conorbergin wrote:
         | 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 wrote:
         | When I first read it I thought it was line comments.
        
           | watersb wrote:
           | Upvoting because similar comments here suggest that you are
           | not alone.
           | 
           | People are having trouble distinguishing between '//' and
           | '\\\'.
        
         | rybosome wrote:
         | 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.
        
         | whitehexagon wrote:
         | 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 wrote:
         | 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.
        
       | qcnguy wrote:
       | 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.
        
         | johnisgood wrote:
         | It is not just syntax that matters. For one I dislike JVM-based
         | languages. I still write it sometimes for work.
        
           | qcnguy wrote:
           | There is also Kotlin/Native, but yes, I am only talking about
           | syntax here.
        
         | lenkite wrote:
         | > 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 wrote:
           | > 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 wrote:
             | 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 wrote:
               | > Instead you execute an ripgrep command with filter
               | directories (or load alias/from history), pipe to neovim
               | 
               | Talk about moving the goal posts.
        
               | lenkite wrote:
               | > 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 wrote:
             | 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.
        
       | IshKebab wrote:
       | > As Zig has only line-comments
       | 
       | Such a good decision. There's no reason to use block comments in
       | 2025.
        
       | kcartlidge wrote:
       | 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 wrote:
         | 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 wrote:
           | 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 wrote:
           | 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`.
        
       | yegle wrote:
       | > 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 wrote:
         | that's not correct.
        
       | ww520 wrote:
       | 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.
        
       | n42 wrote:
       | 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.
        
         | Twey wrote:
         | > 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 wrote:
           | 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 wrote:
             | 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.
        
         | scuff3d wrote:
         | 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.
        
       | melodyogonna wrote:
       | 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.
        
       | culebron21 wrote:
       | 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.
        
       | MrBuddyCasino wrote:
       | > I think Kotlin nails it: val, var, fun.
       | 
       | Been saying this, and I find it strange how few agree.
        
       | norir wrote:
       | 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.
        
       | ruuda wrote:
       | > 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 wrote:
         | 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.
        
         | bmacho wrote:
         | I think you are correct, and it is indeed the name of the 1
         | element type what should go there. However
         | https://en.wikipedia.org/wiki/Unit_type says that Rust has it
         | but not really, for me                 fn sayhi () -> () {
         | println!("hi");       }            fn main() -> ()  {
         | sayhi( sayhi() )       }
         | 
         | does not work.
        
         | cornstalks wrote:
         | 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.
        
       | ivanjermakov wrote:
       | > 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.
        
       ___________________________________________________________________
       (page generated 2025-08-10 23:01 UTC)