Generics are cool. But I dont like to rely on duck typing when I am reading code, its messy. What are the function signatures I should be following? Is there any generic function that is expected to be used there?
Playing around with @yaksher with how semantic we could make generics, we ended up with a simple pattern, that achieves 3 things:
- No runtime costs;
- Call site has total control on how the functions behave, as long as they have the expected function signature;
- Library checks if the interface was used.
So, sily example with a IWriter
interface that checks if a type is a Writer:
const std = @import("std");
// Interface that we will implement on callsite and verify usage on library
pub const IWriter = struct {
const name = "IWriter";
fn is(comptime T: type) bool {
return @hasDecl(T, "isIWriter") and T.isIWriter == IWriter;
}
// specify what the implementation entails here
// depending on the usecase, you could have wrapper functions specified here instead of a simple assignment
pub fn impl(comptime T: type, comptime Impl: type) type {
return struct {
const isIWriter = IWriter;
const writeAll: fn (T, []const u8) anyerror!void = Impl.writeAll;
};
}
};
pub fn implements(comptime T: type, comptime I: type) void {
if (!I.is(T)) {
@compileError("Type does not implement `" ++ I.name ++ "`. (tip: wrap your instance with [your impl of "++ I.name ++ "](...).");
}
}
// library function that verifies if the interface was used
pub fn write(w: anytype, data: []const u8) !void {
comptime implements(@TypeOf(w), IWriter); // comptime, otherwise all the compile errors of how `w` misses the decls will also show up
try w.writeAll(data);
}
// Callsite usage of how to create a type that implements an interface
const FileWriter = struct {
writer: std.fs.File.Writer,
usingnamespace IWriter.impl(FileWriter, struct {
fn writeAll(self: FileWriter, data: []const u8) anyerror!void {
try self.writer.writeAll(data);
}
});
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const writer = FileWriter{ .writer = stdout };
try write(writer, "Hello world!");
}
And just like that, you get the most convoluted "Hello World" I ever written.
Top comments (3)
That's a brilliant idea
Thank you for sharing such a beautiful implementation
Very Informative. Thankyou for sharing I will implement this in my friends website which is the best wikipedia services provider company in United Kingdom.