#Passing only first comptime argument to a function

1 messages · Page 1 of 1 (latest)

paper canopy
#

Hiya,
I often run into situations where I'd like to pass only the first comptime argument of a function. I want to know if there's an terse way to do this in function scope. I have a minimal example.

    pub fn distanceFrom(self: Vec3, other: Vec3) f64 {
        // v I wish that was more concise  
        const powf = struct {
            fn powf(a: f64, b: f64) f64 {
                return std.math.pow(f64, a, b);
            }
        }.powf;

        const sqrt = std.math.sqrt;
        const out = sqrt(
            powf(other.x - self.x, 2) +
            powf(other.y - self.y, 2) +
            powf(other.z - self.z, 2)
        );
        return out;
    }

Here I would like to assign the std.math.pow function to powf with the first argument passed as f64. The only way I can think of doing it here involves a shim function and a temporary struct. A single line like const powf = std.math.pow(f64) would be desirable, but I've checked and that's a syntax error. This is a minimal example, this desire comes up quite often when I am using generic types from the standard library.

Is anything like this possible?

acoustic kayak
#

you can make a wrapper generator with some reflection and @call, i dont think itll be pretty lol

#

oh, maybe you can't if the return type is generic. this works for non-generic return types, but math.pow specifically needs the generic return type:

fn wrap(f: anytype, T: type) fn (anytype) @typeInfo(@TypeOf(f)).@"fn".return_type.? {
    const ctx = struct {
        fn wrappedFunc(args: anytype) @typeInfo(@TypeOf(f)).@"fn".return_type.? {
            return @call(.auto, f, .{T} ++ args);
        }
    };
    return ctx.wrappedFunc;
}

pub fn main() !void {
    const func = wrap(genFunc, f32);
    std.debug.print("{}\n", .{func(.{10})});
}

fn genFunc(T: type, a: T) f32 {
    return a;
}

using @TypeOf(@call(.auto, f, @as(std.meta.ArgsTuple(@TypeOf(f)), undefined))) instead also doesn't work with generic return types

#

i think you need a third param for generic return types:

fn wrap(f: anytype, T: type, R: type) fn (anytype) R {
    const ctx = struct {
        fn wrappedFunc(args: anytype) R {
            return @call(.auto, f, .{T} ++ args);
        }
    };
    return ctx.wrappedFunc;
}

pub fn main() !void {
    const powf = wrap(std.math.pow, f32, f32);
    std.debug.print("{}\n", .{powf(.{ 10, 2 })});
}
paper canopy
acoustic kayak
#

i dont think you can get the “boilerplate” out tbh, for math.pow you avoid typing f32 but then have to put the arguments in a tuple. you could get rid of the tuple by special casing some numbers of params ig

paper canopy
round fulcrum
#

i wouldn't really recommend doing this tho; writing a wrapper is fine when u need it, and tbh u shouldn't really need it that often

round fulcrum
paper canopy
round fulcrum
#

(and actually, that's not quite the same cuz it needs a void context arg as well as specifying the type)

paper canopy
# round fulcrum where are u running into it? i think the only place i've ever encountered it is ...

Here's another example. Say I want to split some strings, and I use the function several times. It would be nice to factor out the generic argument with something like const splitString = std.mem.splitSequence(u8, ...);.

This situation of using a particular standard library function for one type 90% of the time comes up everywhere. It's not that any single instance is cumbersome to write, it just makes sense to factor out repeated logic like this

crystal walrus
#

if you want to do that, just make fn splitString(...) _ReturnType { return std.mem.splitSequence(u8, ...); }. As long as you don't make it pub fn it won't be visible anywhere else anyways.

paper canopy
# round fulcrum why?

Why what? Why factor out repeated information? Idk to me it seems self evident that it's a good ideal here

craggy trellis
#

typing u8 wont kill u lol

round fulcrum
paper canopy