Live data from Hacker News

Zig's Lovely Syntax

matklad.github.io

141–150 of 246 posts

Re: Zig's Lovely Syntax

#141
post #58

Earlier quoted context omitted.

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.

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?

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

Re: Zig's Lovely Syntax

#142
post #54

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

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.

Re: Zig's Lovely Syntax

#143

Earlier quoted context omitted.

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

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.

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

Re: Zig's Lovely Syntax

#144
post #91

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 d…

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.

Re: Zig's Lovely Syntax

#145
post #126
post #42

Earlier quoted context omitted.

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

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.

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.

Re: Zig's Lovely Syntax

#146
post #54

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

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 i…

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.

Re: Zig's Lovely Syntax

#147
post #145
post #126

Earlier quoted context omitted.

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.

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…

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

Re: Zig's Lovely Syntax

#149

Earlier quoted context omitted.

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

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

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.

Re: Zig's Lovely Syntax

#150

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 = $""" {title} Welcome to {sitename}. """; And it even allows for using embedded curly braces as real characters: string json = $$""" {{title}} Welcome to {{sitename}}, which uses the {sitename} syntax. """; The $ (meaning to interpolate curly braces) appears twi…

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

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.
Post reply on HN