Return from recursive loops using ?void

A little follow up…

Now I am contemplating a little variant on this.

In another recursive routine (alpha beta algorithm) I calculate a score. Something like this:

fn search(depth: u8) i32 {
    if (depth == 0) return evaluation();
    if (self.check_timeout()) return 0; // if timeout self.stopped = true
    while (...) {
        const score = -search(depth - 1);
        if (self.stopped) break;
        // code
    }
}

Using ?i32 as return value in case of a timeout is syntactically inconvenient.
I once saw Andrew write something like “errors are for control flow” so what about this:

fn search(depth: u8) TimeOut!i32 {
    if (depth == 0) return evaluation();
    if (self.check_timeout()) return TimeOut.stopped.
    while (...) {
        const score = -search(depth - 1) catch break;
        // 
    }
}

const TimeOut = error { stopped };

In the program it saves me a whole buch of if (self.stopped) checks.
Coming from other languages it feels like “abusing exceptions” because it is not an exception but a normal state of the program.
Are we abusing here or is it just OK?

1 Like

Ouch… compile error: negation of type... Very strange…

I might do it like this:

fn search(self: @This(), depth: u8) TimeOut!i32 {
    if (depth == 0) return evaluation();
    try self.timeout();
    while (...) {
        const score = -self.search(depth - 1) catch break;
        // 
    }
}

// typo fixed, thx @Sze
fn timeout(self: @This()) TimeOut!void {
    if (self.checkTimeout()) return TimeOut.Stopped;
}

const TimeOut = error { Stopped };

The notion of using an error to handle this kind of condition seems basically sound to me. Error just means “if this happens, we can’t keep doing what we’re doing”, a timeout is a relevant case of that pattern.

2 Likes

typo? instead fn timeout?

1 Like
const score = -self.search(depth - 1) catch break

the problem is that this does not compile!

Then show the error with its details, don’t make us imagine the error you could have gotten.

fixed, thanks :slight_smile:

1 Like

error: negation of type '@typeInfo(@typeInfo(@TypeOf(main.search)).@“fn”.return_type.?).error_union.error_set!i32

    var v: i32 = 42;
    v = -search(v) catch {};
fn search(v: i32) !i32 {
    return -v;
}

Edit: in other words: we cannot negate an error…

reproduction:

const std = @import("std");

fn search(v: i32) !i32 {
    return -v;
}

pub fn main() !void {
    const v = -search(5) catch {};
    std.debug.print("v: {}\n", .{v});
}

possible fixes:

pub fn main() !void {
    const v = -(search(5) catch {});
    std.debug.print("v: {}\n", .{v});
}
pub fn main() !void {
    const val = search(5) catch {};
    const v = -val;
    std.debug.print("v: {}\n", .{v});
}

ohoh yes… oh yes… i am so brainless

Don’t be too hard on yourself, I’m over on the Zig operator precedence table scratching my head about this one.

I think it means that catch is right-associative?

Anyway, if it works, ship it.

1 Like

Hahahaha.
Last question. I need the TimeOut to be just TimeOut without any enums involved. Is that possible?
const TimeOut = error;

Edit: return TimeOut :slight_smile:

const TimeOut = error.TimeOut;

Should be fine. A singleton error is a valid error union.