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?
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?
This is indeed a point which makes Zig inflexible.
fn add(x: i32, i32) i32
they have said perma-goodbye to lambdas. They should have at-least considered fn add(x: i32, i32): i32Some 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.
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.
Go has a similar function declaration, and it supports anonymous functions/lambdas.
E.g. in go, an anonymous func like this could be defined as
foo := func(x int, _ int) int { … }
So I’d imagine in Zig it should be feasible to do something like
var foo = fn(x: i32, i32) i32 { … }
unless I’m missing something?
See
https://github.com/golang/go/issues/59122
https://github.com/golang/go/issues/21498
res := is.Map(func(i int)int{return i+1}).Filter(func(i int) bool { return i % 2 == 0 }).
Reduce(func(a, b int) int { return a + b })
vs res := is.Map((i) => i+1).Filter((i)=>i % 2 == 0).Reduce((a,b)=>a+b)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.
Why don't they just add lambdas?
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/
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?
If you do want it, you have the option to, say, heap allocate.
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.
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.
Heap allocating them is fairly rare, because most usages are in things like combinators, which have no reason to enlarge their scope like that.
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>.
https://ziglang.org/documentation/master/std/#std.sort.binar...