andrewCodeDev / Fluent

Fluent interface for REGEX, iteration, and algorithm chaining.
MIT License
82 stars 3 forks source link

usingnamespace removal #24

Open pierrelgol opened 1 month ago

pierrelgol commented 1 month ago

The Zig compiler maintainers intend to maybe remove usingnamespace Remove usingnamespace #20663 the Zig language, as such I propose we discuss how we should deal with it. So far I've been working on a branch that extracts everything into it's own file, where declarations are then imported back manually where needed. I suspect not everything will be able to fit that model, but this will simplify working on the main file.

AndreasHefti commented 1 month ago

I heavily use usingnamespace for Mixins. It's very convenient and also readable in my opinion.

I don't like the suggested alternative for Mixins very much because its lacks readability and one would have to change a lot on the caller site as well as on the receiver side. This would lied to more boilerplate code as well.

If usingnamespace gets removed I hope at least there will be a good alternative for Mixins. Since Mixins are a well known concept I suggest just that:

pub fn CounterMixin(comptime T: type) type {
    return struct {
        pub fn incrementCounter(x: *T) void {
            x._counter += 1;
        }
        pub fn resetCounter(x: *T) void {
            x._counter = 0;
        }
    };
}

pub const Foo = struct {
    _counter: u32 = 0,
    pub mixin CounterMixin(Foo);
};
andrewCodeDev commented 1 month ago

I don't like the suggested alternative for Mixins very much because its lacks readability and one would have to change a lot on the caller site as well as on the receiver side. This would lied to more boilerplate code as well.

We're moving towards atomizing the implementations where the front ends essentially import what they use. Then the entry point has a single switch that breaks over the child type to the output struct. This works because const declarations can be both used like class 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,
    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 eq can be arbitrarily specialized and written only once. We'll then compose our way back up to how we were before. A bit more verbose but @pierrelgol and I are happy with it.

AndreasHefti commented 1 month ago

Yes, this is more readable. On the other hand, still, when you have Mixins with several functions, you need to declare constants for every function of the Mixin in every use of the Mixin. But surely better as the original alternative, thank you for the example.

pierrelgol commented 1 month ago

Hi @AndreasHefti, while the pattern and fundamental design we were using need to evolve, we will, of course, do our best to provide a solution that pleases everyone. Nothing is set in stone. This morning, @andrewCodeDev and I were discussing how to address this issue, and we have decided to proceed with the aforementioned pattern. Naturally, only hands-on experience will reveal how it performs at the scale of our small library. We would be grateful for any feedback or suggestions of course.

andrewCodeDev commented 1 month ago

In fact, I opened a thread for this on Ziggit: https://ziggit.dev/t/mxin-alternative-using-const-declarations/6091