Skip to content(if available)orjump to list(if available)

Zig's Lovely Syntax

Zig's Lovely Syntax

172 comments

·August 10, 2025

ruuda

> 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

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.

n42

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

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

> 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

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

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.

bscphil

> 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

> 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

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

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__

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

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

> 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...

ivanjermakov

> 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.

z_open

> 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

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?).

konart

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

    `A
       simple
          formatted
             string
    `

?

rybosome

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.

z_open

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

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

IshKebab

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

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

.. it is using \\

norir

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

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

Blackarea

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

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.

n42

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.

whitehexagon

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.

hardwaregeek

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

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

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

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.

steveklabnik

I had the exact opposite reaction.

kcartlidge

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

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 :-)

winwang

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`.

gf000

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.

phplovesong

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

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

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.

tialaramex

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]

hinkley

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

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

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

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

A language hostile to tooling/LSP/intellisense.

flohofwoe

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).

pton_xd

"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

Normal function declarations.

This is indeed a point which makes Zig inflexible.

lenkite

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

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.

tux1968

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

flohofwoe

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

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

flohofwoe

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).

jmull

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

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?

jmull

> 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.

flipgimble

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/

nromiun

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

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

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

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

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

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.

ww520

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

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

flohofwoe

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

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

Cyph0n

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

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?

brabel

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/

nromiun

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.

hinkley

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

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?!

tmtvl

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.