Comptime values at the module level

I’m wondering if there is a way to have comptime defined constants in a module.

Use case: a module contains various functions that operates on vectors of size S. When you @import it, you define S depending on your needs.

I guess in C world it would go like this:

// mylib.h
#ifndef VECTOR_SIZE
#define VECTOR_SIZE 32
#endif

// some functions using VECTOR_SIZE

// main.c
#define VECTOR_SIZE 64
#include "mylib.h"
// ...

IIRC @import turns a source file into a struct. As parametric structs are defined as functions taking comptime parameters and returning a type, is it possible to do the same at the module level? Would it even make sense?

For the record I’m aware that I could just define a parametric struct and add all my functions to it in order to make them parametric.

I think that is the way it is done in zig. Allowing both structs and functions for modules would probably make the implementation just needlessly more complex (I haven’t looked at the code in depth, but I would guess that).

Also would be awkward if you do this:

const mod = @import("some-module-I-wanna-use")
mod.callSomeFunc();

and you get an error telling you about some function not having a field “callSomeFunc” or something.

Basically I think it would be awkward to have the split of some modules being used like this @import("name").func() while others would be used like this @import("name")("some-parameters", u32).func().

I am totally clueless on the implementation complexity it would cause, I’ll take your for it.

On the awkward error, as someone who started using Zig two weeks ago, that kind of awkwardness is already there in the language as soon as you start using parametric types. Basically you have a function that is named like a struct, and the language syntax doesn’t make it self evident IMO.

I can’t count how many times I forgot the comptime parameters of a type, got a weird error and though “ah yes, that one was a function building a type, not a struct”.

So you have to get used to it anyway, which is why I thought modules behaving like parametric structs made sense.

I think something like this looks reasonable:

const mod = @import("some-module-I-wanna-use").use(.{ vector_size = 64 });
const ModuleParams = struct {
    vector_size: usize = 32,
    vector_width: usize = 4;
};

pub fn use(comptime params: ModuleParams) type {
    return struct {
        pub const ET = @Type(.{ .Float = .{ .bits = params.vector_size } });
        pub const VT = @Vector(params.vector_width, ET);

        pub fn add(a: VT, b: VT) VT {
            return a + b;
        }

        // ...etc
    };
}

Bit of a bummer with the added indentation, but still more readable than C + macros.

1 Like