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.
z_open · 21m ago
> Raw or multiline strings are spelled like this:
const still_raw =
\\const raw =
\\ \\Roses are red
\\ \\ Violets are blue,
\\ \\Sugar is sweet
\\ \\ And so are you.
\\ \\
\\;
\\
;
This syntax seems fairly insane to me.
IshKebab · 11m ago
Maybe if you've never tried formatting a traditional multiline string (e.g. in Python, C++ or Rust) before.
If it isn't obvious, the problem is that you can't indent them properly because the indentation becomes part of the string itself.
Some languages have magical "removed the indent" modes for strings (e.g. YAML) but they generally suck and just add confusion. This syntax is quite clear (at least with respect to indentation; not sure about the trailing newline - where does the string end exactly?).
z_open · 5m ago
Even if we ignore solutions other languages have come up with, it's even worse that they landed on // for the syntax given that it's apparently used the same way for real comments.
fcoury · 16m ago
I really like zig but that syntax is indeed insane.
bscphil · 1h ago
> Like Rust, Zig uses 'name' (':' Type)? syntax for ascribing types, which is better than Type 'name'
I'm definitely an outlier on this given the direction all syntactically C-like new languages have taken, but I have the opposite preference. I find that the most common reason I go back to check a variable declaration is to determine the type of the variable, and the harder it is to visually find that, the more annoyed I'm going to be. In particular, with statically typed languages, my mental model tends to be "this is an int" rather than "this is a variable that happens to have the type 'int'".
In Rust, in particular, this leads to some awkward syntactic verbosity, because mutable variables are declared with `let mut`, meaning that `let` is used in every declaration. In C or C++ the type would take the place of that unnecessary `let`. And even C (as of C23) will do type inference with the `auto` keyword. My tendency is to use optional type inference in places where needing to know the type isn't important to understand the code, and to specify the type when it would serve as helpful commentary when reading it back.
SkiFire13 · 1h ago
> In C or C++ the type would take the place of that unnecessary `let`
In my opinion the `let` is not so unnecessary. It clearly marks a statement that declares a variable, as opposed to other kind of statements, for example a function call.
This is also why C++ need the "most vexing parse" ambiguity resolution.
reactordev · 47m ago
I’m in the same boat. It’s faster mentally to grok the type of something when it comes first. The name of the thing is less important (but still important!) than the type of the thing and so I prefer types to come before names.
From a parser perspective, it’s easier to go name first so you can add it to the AST and pass it off to the type determiner to finish the declaration. So I get it. In typescript I believe it’s this way so parsers can just drop types all together to make it compatible with JavaScript (it’s still trivial to strip typing, though why would you?) without transpiling.
In go, well, you have even more crazier conventions. Uppercase vs lowercase public vs private, no inheritance, a gc that shuns away performance minded devs.
In the end, I just want a working std library that’s easy to use so I can build applications. I don’t care for:
type Add<A extends number, B extends number> = [
…Array<A>,
…Array<B>,
][“length”]
This is the kind of abuse of the type system that drives me bonkers. You don’t have to be clever, just export a function. I don’t need a type to represent every state, I need intent.
AnimalMuppet · 4m ago
I, too, go back up the code to find the type of a variable. But I see the opposite problem: if the type is first, then it becomes harder to find the line that declares the variable I'm interested in, because the variable name isn't first. It's after the type, and the type could be "int" or it could be "struct Frobnosticator". That is, the variable name is after a variable-length type, so I have to keep bouncing left to right to find the start of the variable name.
erk__ · 26m ago
Maybe it just have to do with what you are used to, it was one of the things that made me like Rust coming from F# it had the same `name : type` and `let mutable name` that I knew from there.
Quekid5 · 18m ago
> I find that the most common reason I go back to check a variable declaration is to determine the type of the variable,
Hover the mouse cursor over it. Any reasonable editor will show the type.
> In Rust, in particular, this leads to some awkward syntactic verbosity, because mutable variables are declared with `let mut`, meaning that `let` is used in every declaration.
Rust is very verbose for strange implementation reasons... namely to avoid parse ambiguities.
> In C or C++ the type would take the place of that unnecessary `let`.
OTOH, that means you can't reliably grep for declarations of a variable/function called "foo". Also consider why some people like using
auto foo(int blah) -> bool
style. This was introduced because of template nonsense (how to declare a return type before the type parameters were known), but it makes a lot of sense and makes code more greppable. Generic type parameters make putting the return type at the front very weird -- reading order wise.
Anyhoo...
phplovesong · 1h ago
I find Zig syntax noicy. I dont like the @TypeOf (at symbol) and pals, and the weird .{.x} syntax feels off.
Zig has some nice things going on but somehow code is really hard to read, admitting its a skill issue as im not that versed in zig.
flohofwoe · 1h ago
The dot is just a placeholder for an inferred type, and IMHO that makes a lot of sense. E.g. you can either write this:
const p = Point{ .x = 123, .y = 234 };
...or this:
const p: Point = .{ .x = 123, .y = 234 };
When calling a function which expects a Point you can omit the verbose type:
takePoint(.{ .x = 123, .y = 234 });
In Rust I need to explicitly write the type:
takePoint(Point{ x: 123, y: 234);
...and in nested struct initializations the inferred form is very handy, e.g. Rust requires you to write this (not sure if I got the syntax right):
...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:
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 · 1h ago
Zig is planning to get rid of explicit `T{}` syntax, in favor of only supporting inferred types.
So the explanation of a dot standing in for a type doesn't make sense in the long run.
flohofwoe · 1h ago
Ah, I wasn't aware of that proposal. But yeah in that case I would also heavily prefer to "drop the dot" :)
IMHO Odin got it exactly right. For a variable with explicit type:
a_variable : type = val;
...or for inferred type:
a_variable := val;
...and the same for constants:
a_const : type : val;
a_const :: val;
...but I think that doesn't fit into Zig's parser design philosophy (e.g. requiring some sort of keyword upfront so that the parser knows the context it's in right from the start instead of delaying that decision to a later time).
nromiun · 56m ago
I like Zig as well, but I won't call its syntax lovely. Go shows you can do pretty well without ; for line breaks, without : for variable types etc.
But sure, if you only compare it with Rust, it is a big improvement.
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 · 39m ago
That is why syntax debates are so difficult. There is no objectively best syntax. So we are all stuck with subjective experience. For me I find Python (non-typed) and Golang syntax easiest to read.
Too many symbols like ., :, @, ; etc just mess with my brain.
Twey · 28m ago
> I think Kotlin nails it: val, var, fun. Note all three are monosyllable, unlike const and fn!
At least in my pronunciation, all five of those are monosyllabic! (/kQnst/, /f@n/).
(Nice to see someone agree with me on minimizing syllable count, though — I definitely find it easier to chunk[1] fewer syllables.)
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.
pton_xd · 1h ago
"Zig doesn’t have lambdas"
This surprises me (as a C++ guy). I use lambdas everywhere. What's the standard way of say defining a comparator when sorting an array in Zig?
jmull · 16m ago
You can declare an anonymous struct that has a function and reference that function inline (if you want).
There's a little more syntax than a dedicated language feature, but not a lot more.
What's "missing" in zig that lambda implementations normally have is capturing. In zig that's typically accomplished with a context parameter, again typically a struct.
tapirl · 45m ago
Normal function declarations.
This is indeed a point which makes Zig inflexible.
tux1968 · 1h ago
Same as C, define a named function, and pass a pointer to the sorting function.
flohofwoe · 58m ago
Unlike C you can stamp out a specialized and typesafe sort function via generics though:
And what if you need to close over some local variable?
flohofwoe · 54m ago
Not possible, you'll need to pass the captured variables explicitly into the 'lambda' via some sort of context parameter.
And considering the memory management magic that would need to be implemented by the compiler for 'painless capture' that's probably a good thing (e.g. there would almost certainly be a hidden heap allocation required which is a big no-no in Zig).
pton_xd · 40m ago
As far as I know lambdas in C++ will not heap allocate. Basically equivalent to manually defining a struct, with all your captures, and then stack allocating it. However if you assign a lambda to a std::function, and it's large enough, then you may get a heap allocation.
Making all allocations explicit is one thing I do really like about Zig.
nine_k · 41m ago
...or escape analysis and lifetimes, but we already have Rust %)
the__alchemist · 1h ago
I wish Zig had lovely vector, quaternion, matrix etx syntax. The team's refusal to add operator overloading will prevent this.
flohofwoe · 51m ago
You don't need operator overloading for vector and matrix math, see pretty much all GPU languages. What Zig is missing is a complete mapping of the Clang Extended Vector and Matrix extensions (instead of the quite limited `@Vector` type):
> 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 · 4m ago
that's not correct.
losvedir · 26m ago
> Note all three are monosyllable, unlike const and fn!
I'm not sure if I'm parsing this right, but is the implication that "const" is not monosyllabic? It certainly is for me. How else do people say it? I get "fn" because people might say "eff enn" but I don't see what the equivalent for const would be.
lvl155 · 29m ago
Zig is just fun to write. And to me, it’s actually what I wish Rust was like. Rust is a great language, and no one’s going to argue that point but writing Zig for the first time was so refreshing. That said, Rust is now basically default for systems and Zig came too late.
throwawaymaths · 3m ago
never underestimate second mover advantage. if zig gets static borrow checking, it would be amazing
IshKebab · 14m ago
> As Zig has only line-comments
Such a good decision. There's no reason to use block comments in 2025.
MrResearcher · 1h ago
It's still not clear to me how you can make two comptime closures with different contents and pass those as a functor into the same function. It needs to have a sort of VTable to invoke the function, and yet since the contents are different, the objects are different, and their deallocation will be different too.
Defining VTable in zig seems to be a pretty laborious endeavor, with each piece sewn manually.
pjmlp · 1h ago
Having @ and .{ } all over the place is hardly lovely, as is having modules like JavaScript's CJS.
Akronymus · 1h ago
It's especially bad on a qwertz keyboard as well.
flohofwoe · 1h ago
That's why "real programmers" use English keyboard layout regardless of the physical keyboard ;)
Curly brace syntax would never have been invented in Europe (case in point: Python and Pascal).
Akronymus · 58m ago
Oh for sure. I am using qwerty myself. And my fav language (f#) has relatively few curly braces.
christophilus · 22m ago
F# is wonderful. I wish someone would make an F# that compiled as fast as OCaml or Go and which had Go’s standard library and simple tooling.
pjmlp · 39m ago
Real programmers deliver business value, regardless of what keyboard they have at their disposal.
Just like great musicians make the difference in the band, regardless of the instruments scattered around the studio.
hardwaregeek · 1h ago
Everyone agrees that "syntax doesn't matter", but implicit in that is "syntax doesn't matter, so let's do what I prefer". So really, syntax does matter. Personally I prefer the Rust/Zig/Go syntax of vaguely C inspired with some nice fixes, as detailed in the post. Judging by the general success of that style, I do wonder if more functional languages should consider an alternative syntax in that style. The Haskell/OCaml concatenative currying style with whitespace is elegant, but sufficiently unfamiliar that I do think it hurts adoption.
After all, Rust's big success is hiding the spinach of functional programming in the brownie of a systems programming language. Why not imitate that?
nromiun · 31m ago
That saying never made any sense to me either. After all syntax is your main interface to a language. Anything you do has to go through the syntax.
Some people say the syntax just kind of disappears for them after some time. That never seems to happen with me. When I am reading any code the syntax gets even more highlighted.
WalterBright · 30m ago
const x: i32 = 92;
D has less syntax:
const int x = 92;
Just for fun, read each declaration out loud.
IshKebab · 15m ago
"less syntax" isn't necessarily better. The goal isn't to have as few characters as possible.
johnisgood · 8m ago
True, it is subjective. I prefer C, Go, PHP, ... and OCaml, Erlang, Elixir, Perl, and sometimes Ada and Common Lisp.
I like the syntaxes of each, although Ada is too verbose to me, and with Factor and Common Lisp I have a skill issue.
WalterBright · 11m ago
I agree. I'm not a fan of minimized syntax (see Haskell). But I am a fan of easy to read syntax!
WalterBright · 23m ago
Zig:
fn ArrayListType(comptime T: type) type {
D:
T ArrayListType(T)() {
dpassens · 41s ago
I don't know D but shouldn't that be
WalterBright · 26m ago
Zig:
const std = @import("std");
D:
import std;
zabzonk · 1h ago
In my experience, everyone finds the syntax of their favourite language lovely - I love (mostly) C++.
aeonik · 51m ago
Not me!
I don't like the syntax of Lisps, with the leading parenthesis to begin every expression.
I use it anyway because it's so useful and powerful. And I don't have any better ideas.
ben-schaaf · 1h ago
As someone who works almost exclusively in C++, the whole "most vexing parse" makes the syntax indefensible.
zabzonk · 19m ago
In my experience, people simply go "Doh, of course!" or don't write the wrong code in the first place. It has never caused me any real problems.
For those of you that may be curious, or not know C++, in C++ this:
Obj x();
is a declaration of a function called x that returns an Obj. Whereas this:
Obj x;
defines (possibly, depending on context) an instance of the type Obj called x.
Most people get over this pretty quickly.
If that is your main complaint about a language then the language doesn't have too many problems. Not that I'm suggesting that C++ doesn't have more serious problems.
guidopallemans · 22m ago
I don't like how Python does lambdas, its indentation-based blocks, how there's both ' and ", I could go on.
jeltz · 1h ago
I am an exception then. Rust may be my favourite language but the syntax is pretty awful and one of its biggest weaknesses. I also love Ruby but I am pretty meh about its syntax.
zabzonk · 1h ago
I don't think many people would start to use a language unless the found the syntax at least a little simpatico - but I guess they could be drawn by the semantics it provides.
sampullman · 57m ago
I (almost) don't consider syntax at all when considering a language, just whether it's suitable for the task.
The exception is languages with syntax that require a non-standard keyboard.
Y_Y · 1h ago
> C uses a needlessly confusing spiral rule
Libellous! The "spiral rule" for C is an abomination and not part of the language. I happen to find C's type syntax a bit too clever for beginners, but it's marvellously consistent and straightforward. I can read types fine without that spiral nonsense, even if a couple of judicious typedefs are generally a good idea for any fancy function types.
Twey · 34m ago
> fancy function types
If the syntax were straightforward, plain old function types wouldn't be ‘fancy’ :)
throwawaymaths · 1h ago
It's not context free though.
Rusky · 12m ago
It is context free, just ambiguous.
qcnguy · 18m ago
Weird the author finds it lovely and compares to Kotlin, but doesn't find Kotlin superior. Kotlin invested heavily in a really nice curly brace syntax. It is actually the nicest out there. In every point the author makes, it feels like Kotlin did it the same or better. For example:
1. Integer literals. "var a = 1" doesn't work, seems absurd. In Kotlin literals do have strong types, but coercion is allowed when defining variables so "var a = 1" works, and "var a: Long = 1" works even though you can write a literal long as 1L. This means you can write numbers to function parameters naturally.
2. Multi-line string literals. OK this is a neat idea, but what about copy/paste? In Kotlin you can just write """ .. """.trimIndent() and then copy paste some arbitrary text into the string, the indent will be removed for you. The IDE will also help with this by adding | characters which looks more natural than \\ and can be removed using .trimMargin(), only downside is the trimming is done at runtime but that could easily be fixed without changing the language.
3. Record literals. This syntax is called lovely because it's designed for grep, a properly funded language like Kotlin just uses named kwargs to constructors which is more natural. There's no need to design the syntax for grep because Kotlin is intended to be used with a good IDE that can answer this query instantly and precisely.
4. Function syntax. "fn foo(a: i32) i32 {}" seems weird. If the thing that has a type and the type are normally separated by a : then why not here? Kotlin does "fun foo(a: Int): Int {}" which is more consistent.
5. Locals. Agree with author that Kotlin nails it.
6. Not using && or ||, ok this one Zig wins, the Zig way is more consistent and reads better. Kotlin does have `and` and `or` as infix operator functions, but they are for the bitwise operations :(
7. Explicit returns. Kotlin supports blocks that return values and also doesn't need semicolons, so not quite sure what the tradeoff here is supposed to be about.
8. Loops being expressions is kinda cool but the Kotlin equivalent of his example is much easier to read still: "val thing = collection.first { it.foo > bar }". It compiles to a for loop due to the function inlining.
9. Generics. Zig's way seems primitive and unnecessarily complex. In Kotlin it is common to let the compiler infer generics based on all available information, so you can just write "someMethod(emptyList())" and emptyList<T>() infers to the correct type based on what someMethod expects.
Overall Zig looks like a lot of modern languages, where they are started as a hobby or side project of some guy and so the language is designed around whatever makes implementing the compiler most convenient. Kotlin is unusual because it doesn't do that. It was well funded from the start, so the syntax is designed first and foremost to be as English-like and convenient as possible without leaving the basic realm of ordinary curly-brace functions-and-oop style languages. It manages to be highly expressive and convenient without the syntax feeling overly complex or hard to learn.
johnisgood · 4m ago
It is not just syntax that matters. For one I dislike JVM-based languages. I still write it sometimes for work.
Western0 · 49m ago
Love Ruby and Cristal syntax ;-)
darthrupert · 44m ago
Absolutely. Kinda of the best of many worlds, because it has what looks like an indentation-based syntax but actually blocks are delimited by start and end keywords. This makes it possible (unlike, say, python) to programmatically derive correct code formatting more reliably.
And no pointless semicolons.
do_not_redeem · 1h ago
Since we're talking syntax... it's mildly infuriating that the zig parser is not smart enough to understand expressions like `const x=a()orelse b();`. You have to manually add a space before `orelse` -- but isn't that what `zig fmt` is for? I have RSI and it's maddening having to mash the arrow keys and add/remove whitespace until the parser is happy.
I've heard the argument that people might confuse binary operators for prefix/postfix operators, but I don't buy it. Who would think an operator named `orelse` is anything but binary?
hmry · 1h ago
"Read the file, or else!" The threatening postfix operator
nateglims · 31m ago
The error when you accidentally pass a variable directly instead of in a .{} is also really unclear.
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?).
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.
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.
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:
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.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
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...
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.
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.
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.
IMHO Odin got it exactly right. For a variable with explicit type:
...or for inferred type: ...and the same for constants: ...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).But sure, if you only compare it with Rust, it is a big improvement.
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.
Too many symbols like ., :, @, ; etc just mess with my brain.
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.
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?
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.
This is indeed a point which makes Zig inflexible.
https://ziglang.org/documentation/master/std/#std.sort.binar...
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).
Making all allocations explicit is one thing I do really like about Zig.
https://clang.llvm.org/docs/LanguageExtensions.html#vectors-...
https://clang.llvm.org/docs/LanguageExtensions.html#matrix-t...
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.
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.
Such a good decision. There's no reason to use block comments in 2025.
Curly brace syntax would never have been invented in Europe (case in point: Python and Pascal).
Just like great musicians make the difference in the band, regardless of the instruments scattered around the studio.
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?
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.
I like the syntaxes of each, although Ada is too verbose to me, and with Factor and Common Lisp I have a skill issue.
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.
For those of you that may be curious, or not know C++, in C++ this:
is a declaration of a function called x that returns an Obj. Whereas this: 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.
The exception is languages with syntax that require a non-standard keyboard.
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.
If the syntax were straightforward, plain old function types wouldn't be ‘fancy’ :)
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.
And no pointless semicolons.
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?