One place in TigerBeetle where we get a lot of friction is that almost everything is a generic function, parametrized by Storage
, IO
, MessageBus
and other types we inject in our simulator:
// src/vsr/replica.zig
pub fn ReplicaType(
comptime StateMachine: type,
comptime MessageBus: type,
comptime Storage: type,
comptime Time: type,
comptime AOF: type,
) type {
This is the right kind friction in general, as generic types are costly; they should be minimized and concrete types should be preferred instead. At the same time, in our context I donât think we can do that, as we pretty fundamentally need different real&fake implementations, and we donât want dynamic dispatch in the real case, because our testing code should not affect production code (there are also performance considerations, but they are secondary here).
One thing I realized though is that we can remove generic parameters, by making them module parameters. If we make src/vsr/replica.zig
and other things modules, than we we can do:
const StateMachine = @import("StateMachine");
const MessageBus = @import("MessageBus");
const Storage = @import("Storage");
const Time = @import("Time");
const AOF = @import("AOF");
I am wondering how horrible would it be to make this pattern first-class, by allowing a file to âimportâ its parameters, turning it into a function?
// src/vsr/replica.zig
const StateMachine,
const MessageBus,
const Storage,
const Time,
const AOF = @import();
// main.zig
const Replica = @import("./src/vsr.replica.zig")(
StateMachine,
MessageBus,
Storage,
Time,
AOF,
);
That is, a file with @import()
is a type-returning function with comptime parameters.