Anonymous functions/lambdas

Is there any way to create anonymous functions and pass them as function arguments, and if not, is something like that planned for some future release?

1 Like

Technically, yes you can but it’s not quite like a Java/C++ lambda (where there is a specific syntax for an anonymous function or closure).

Basically, because struct declarations are anonymous, you can declare a struct and return the member function from said struct. So for instance, in the HashMap implementation, here you can see they are creating a generic equality function:

pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K) bool) {
    return struct {
        fn eql(ctx: Context, a: K, b: K) bool {
            _ = ctx;
            return meta.eql(a, b);
        }
    }.eql;
}

Now this example is stateless so you’ll probably want to play around with stateful alternatives.

Basically Zig really, really does not like to allocate things if it isn’t absolutely necessary and a lot of closure implementations will take that liberty on your behalf - Zig won’t. Besides, you’d have to pass an allocator to the closure itself to give it the capability to allocate in the canonical sense. I don’t see closures making it into the language any time soon or even at all.

Furthermore, a lot of lambda-function like behavior can be achieved using meta programming. So for instance, I can create a comptime parameter of anytype “comptime foo: anytype” and then use that to generically dispatch on internals. I do a lot of that here: ZEIN/src/tensor_ops.zig at main · andrewCodeDev/ZEIN · GitHub

For instance, I am dispatching on different arithmetic functions generically for AVX instructions:

arithmeticDispatch(addGeneric, x, y, z); // addition dispatch
arithmeticDispatch(subGeneric, x, y, z); // subtraction dispatch
arithmeticDispatch(mulGeneric, x, y, z); // multiplication dispatch

The two approaches can be combined to make lambda-esque functionality but to answer your question, no - there is not a current specified and particularized lambda syntax.

5 Likes

I remember seeing somewhere a proposed syntax like cons foo = fn() void {... to declare functions and at the same time allow for closures. But I can’t find it now and don’t remember where I saw that. :face_with_spiral_eyes:

Probably here:

1 Like

syntax yes, closures probably not, it’s a footgunny feature in a programming language like zig

3 Likes

Awesome. While the syntax with anonymous functions using structs is a bit cumbersome, it still allows me to not have to define every predicate as a separate function and makes the code nice and tidy. A little example for anyone interested:

const std = @import("std");

pub fn all(comptime T:type, buf: []const T, comptime predicate: (fn (val: T) bool)) bool {
    for (buf) |val| {
        if (!predicate(val))
            return false;
    }

    return true;
}

test "test all" {
    const arr_1 = [_]i32{1} ** 5;
    try std.testing.expect(
        all(i32, arr_1[0..], struct {
            pub fn eql(val: i32) bool { return val == 1; }
        }.eql)
    );
}

I haven’t used anytype here because showing an explicit type when reading the function signature just makes it a little more undestandable for me.

1 Like

I like to use the for as an expression when dealing with predicates, but it’s just a matter of code style preference:

return for (buf) |val| {
    if (!predicate(val)) break false;
} else true;
2 Likes

Ah okay, I’m still getting used to what Zig has to offer, didn’t occur to me to use a for expression, even tho I prefer expressions to statements myself. :grinning: Thanks!

1 Like