Mixin alternative using const declarations

@pierrelgol and I are working on replacing usingnamespace for Fluent (and just a general refactor and expansion) and tossed some ideas back and fourth.

One idea that we’re converging on is using public const declarations as they can be used as both class level and instance level functions:

const std = @import("std");

pub fn eq_impl(comptime T: type) type {   
    return struct {
        pub fn call(self: T, other: T) bool {
            return self.data == other.data;
        }  
    };
}

const Bar = struct {
    data: i32 = 42,
    // here's where the implementation gets "mixed-in"
    const eq = eq_impl(Bar).call;
};

pub fn main() !void {

    const x: Bar = .{ .data = 42 };
    const y: Bar = .{ .data = 42 };

    if (x.eq(y)) // instance level
        std.log.info("It worked", .{});
    
    if (Bar.eq(x, y)) // type level
        std.log.info("It worked", .{});

    return;
}

Here, you can see that the public const declaration allows us to import an implementation that can be used both as a class level and instance level function implementation. We’re pretty happy with this and will use it in its most basic form to prevent code duplication, but I’m curious how far this can go.

7 Likes

Just to note, you can extend this quite easily using comptime function parameters that take in your meta-functions to be specialized. The Bar example is highly trivial but it matches our use case for Fluent - not true in general but just for demonstration’s sake.

A little more involved would be something like so… (pseudo-code):

pub fn MyImpl(comptime Self: type) type {
    return struct {
        pub fn foo(self: Self, ...) ...
        pub fn bar(self: Self, ...) ...
    };
}

pub fn Thing(comptime T: type, comptime Impl: anytype) type {
    return struct {
        const Self = @This();     
        const foo = Impl(Self).foo;
        const bar = Impl(Self).bar;
    };
}

const Widget = Thing(u8, MyImpl);

The major difference is there’s no “silent-injection” as it were. You have to specifically declare foo, bar, etc... in the interface.

6 Likes