4 ms·
I did it this way so that the implementation doesn't need to know about the interface at all. The programmers can string the interface and implementation togeth
by ww520 1y ago
I did it this way so that the implementation doesn't need to know about the interface at all. The programmers can string the interface and implementation together with,
const intf = interface.implBy(implementation)
This makes it possible to write an interface against any implementation objects. E.g. I can write an interface against the standard hashmap's get() and put() methods. Why would I do that? Let's say I have my custom hashmap with get() and put(), but I want to use the standard hashmap as fallback. A common interface working against my custom hashmap and the standard hashamp is possible.
- kingstnap 1y ago"The implementation doesn't need to know about the interface" Lol sure. It "doesn't need to know" it just happens to magically have the correct functions as public methods to exactly map into your vtable constructor. It's much easier to not do this. And have your implementations provide the vtable as a method. If your implementation doesn't provide a method for this you need to wrap it with an object that does. Which you've done but you've decided that wrapper object should be the interface itself which is weird.
- ww520 1y agoYou get back an interface object at the end anyway. Why force the implementation to produce it? And if you have a type implementing 5 different interfaces, like one for Comparable, one for Iteratible, etc., do you want to stick 5 interface constructors in the implementation?
- kingstnap 1y ago> Do you want to stick 5 interface constructors in implementation Yes. It's not as crazy as you might imagine, and it's more robust than the log delegator approach.
- ameixaseca 1y agoThere's a false dichotomy in this whole discussion: for instance, a third option is having the interface as a separate declaration. A simple example using traits (Rust): // logger.rs trait Logger { fn log_something(&self); } // mycustomlogger.rs struct MyCustomLogger; // logger.rs or mycustomlogger.rs impl Logger for MyCustomLogger { fn log_something(&self) { println!("Hello"); } } The actual `log_something` implementation for `MyCustomLogger` (ie, the block with the `println!` call) can be outside of the impl, as a function or inherent method, and just be called by the impl block - thus implementing similar "glue" as described in the article. The difference is that there is no strong requirement on where exactly it needs to be [1]. This would likely require Zig to support "interfaces" at the language level, though. -- [1] PS: there is the "orphan rule" for trait implementations, but I'm simplifying it here for the sake of the example EDIT: formatting
- ww520 1y agoThanks for bringing new perspective into the discussion. Your Rust code is a good example. I did take Rust for inspiration. The Zig version ‘Interface1.implBy(Impl2)’ is a rough equivalent to ‘impl Interface1 for Impl2’. It did it within the constraints of Zig.
- discreteevent 1y ago> Lol sure. It "doesn't need to know" it just happens to magically have the correct functions This is called structural typing. Go and typescript have forms of it.