Live data from Hacker News

Zig's Lovely Syntax

matklad.github.io

231–240 of 246 posts

Re: Zig's Lovely Syntax

#231
post #212

Earlier quoted context omitted.

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.

I know how Zig works. `type` is some type the function will return, you must look at the implementation to know what actually got returned, given the comptime arguments given to it by the caller (as I already mentioned). Where is the misunderstanding??

Well, your example seems to do something completely different, which is return an ArrayList!T rather than a type.

Re: Zig's Lovely Syntax

#232
post #201
post #195

Earlier quoted context omitted.

It (this particular example, of function pointer syntax) is absolutely just incidental complexity, though. E.G. Haskell (a -> b) -> c -> d becomes C D (*f(B (*)(A)))(C) and it's no surprise that the former is considered much less fancy than the latter. Of course it's not common — because the language makes it painful :) The causation is the other way around. We've seen in plenty of languages that if first-class funct…

Without curring and closures it certainly will be more painful! I might write the equivalent signature, D f(A, B, C) and then reorganize things to just pass f around, or make a struct if you really want to bake in your first function.

Right, due to the complexity of the syntax one of the most sensible things to do in C if you're faced with a problem that maps naturally to higher-order functions is to reframe the problem so that the solution doesn't use higher-order functions — basically doing a compilation step yourself. D (*f(B (*)(A)))(C) is definitely a fancy type, after all :)

Re: Zig's Lovely Syntax

#233

Earlier quoted context omitted.

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

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

If the lambda is a value type, you can just store whatever captures you want in the fields of this type, no need for heap allocations - they'll go on the stack just like anything else. You can even ask the user to explicitly specify which variables to capture, like in C++ lambdas, to be very explicit about the size of the lambda structure.

Re: Zig's Lovely Syntax

#234
post #167

Earlier quoted context omitted.

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

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

> you can't just add it after the fact.

why do you say that?

https://www.youtube.com/watch?v=ZY_Z-aGbYm8

Re: Zig's Lovely Syntax

#235
post #110
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…

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

As someone who loves Lisps, I still have to disagree on the value of the s-expression syntax. I think that sexps are very beautiful, easy to parse, and easy to remember, but I think that overall they're less useful than Algol-like syntaxes (of which I consider most modern languages, including C++, to be in the family of), for one reason:

Visually-heterogeneous syntaxes, for all of their flaws, are easier to read because it's easier for the human brain to pattern-match on distinct features than indistinct ones.

Re: Zig's Lovely Syntax

#236

Earlier quoted context omitted.

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

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.

That's... honestly really disappointing. I use explicit `T{}` because otherwise becomes too unreadable, too Assembly-like: I like knowing what types I'm using. It also provides a convenient thing to click on to inspect the type. I genuinely do not understand this headlong pursuit of conciseness to the detriment of readability.

Re: Zig's Lovely Syntax

#237
post #96

Earlier quoted context omitted.

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.

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

I used to be very enthusiastic about generic types. Now, well what else would you do? I don’t mean that as a rhetorical question. If someone came up with another way to represent functions that can take multiple types and knows what it will return, I’d be all over them.

Elixir is trying something, I don’t know yet whether it will be better. But their solution is based on a decision about how to do overloading that I suspect makes for maintenance problems later. So it’s gonna have to be good to offset the consequence.

Re: Zig's Lovely Syntax

#238
post #212

Earlier quoted context omitted.

I know how Zig works. `type` is some type the function will return, you must look at the implementation to know what actually got returned, given the comptime arguments given to it by the caller (as I already mentioned). Where is the misunderstanding??

Well, your example seems to do something completely different, which is return an ArrayList!T rather than a type.

It achieves the same result in practice. The reason Zig needs to return a type is because it lacks a way to represent `T!A` where T is a parameterized type, and A is the parameter. In this case, that would've been better because you would be able to tell exactly what the type being returned was.

If you must return different types depending on the argument in D, it's also possible.

Here's a silly example:

    struct ArrayList(T, alias capacity) {
      private T[capacity] array;
      private uint _length;
      uint length() const => _length;
      T get(uint index) const => array[index];
      void add(T t) {
        array[_length++] = t;
      }
    }

    struct EmptyList(T) {
      uint length() const => 0;
    }

    /// This will return a different type depending on the length argument,
    /// which is like a Zig comptime argument.
    /// We cannot return the type itself, but the result is very similar.
    auto createArrayList(T, alias length)() {
      static if (length == 0) {
        return EmptyList!T();
      } else {
        return ArrayList!(T, length)();
      }
    }

    void main()
    {
      import std.stdio;
      auto empty = createArrayList!(int, 0);
      writeln(empty);
      auto list = createArrayList!(int, 2);
      list.add(5);
      list.add(6);
      writeln(list);
    }
Result:

    EmptyList!int()
    ArrayList!(int, 2)([5, 6], 2)

Re: Zig's Lovely Syntax

#239
post #163

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

The void type has considerable heritage, dating back all the way to ALGOL 68, and is traditionally defined as having one member:

> The mode VOID has a single value denoted by EMPTY.

Re: Zig's Lovely Syntax

#240

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

Thanks for the correction. I never read the spec, just started using it. And as I tend to balance my first and last line indentation I never realised.
Post reply on HN