digitalmars.D - "External interfaces" or "Interface maps"
- Hxal (81/81) Oct 17 2008 =========================== External interfaces ========================...
- Steven Schveighoffer (11/20) Oct 17 2008 I am not sure, but I don't think interfaces are 'fat' pointers. I think...
- Andrei Alexandrescu (14/22) Oct 17 2008 [snip]
- Hxal (8/16) Oct 18 2008 I was really looking at the possibility of adapting objects to interface...
- KennyTM~ (23/121) Oct 18 2008 super.FancyNameForOpen(args); //?
- Hxal (6/8) Oct 18 2008 The whole proposal is moot in light of interfaces in D using
- Michel Fortin (8/59) Oct 18 2008 This reminds me of an old proposal of mine (to which no one replied).
=========================== External interfaces =============================== The idea is to allow implementing an interface for a class, outside the class' definition. This is possible as I understand, because interfaces are "fat" pointers -- they consist of an object reference and the interfaces' vtable for the particular class, which is separate from the class' vtable. Therefore it should be possible to create an interface vtable outside the module that declares the class. This would allow us to adapt classes to interfaces from different libraries. Example: | import foreign.io : ForeignFile = File; | | import my.io : IFile; | | interface IFile for ForeignFile | { | void open (args) | { | object.FancyNameForOpen (args); | // typeof(this) == IFile | // typeof(object) == ForeignFile | // (cast(IFile) object) == this | // I've used the name 'object', but that's really arbitrary, | // it's just important that this variable was defined by the compiler | // so that every method invocation didn't perform a dynamic cast | } | | /* et cetera */ | } | | void example () | { | IFile file = cast(IFile) new ForeignFile (); | } Since supposedly D object/interface casts are always dynamic, there should be no problem with casting even if the cast site doesn't directly see the external interface declaration at compile time. External interfaces should be equivalent to regular interface implementations in respect to the inheritance hierarchy, and should be freely intermixable. AFAIK it's possible to implement, because the mapping of implemented interfaces can be built during runtime initialization. Potential problems that need to be solved include: - Two external interface definitions for the same interface and class pair in different modules; it'd be nice if the compiler could detect that, but I'm not sure if it's at all possible Advantages compared to wrapper classes: - No additional objects need to be allocated Disadvantages compared to wrapper classes: - No additional state can be kept ======================== Interfaces for other types =========================== The same mechanism could be used to implement interfaces for pointers to arbitrary types (not for value types themselves, as the object field of the interface instance is restricted to pointer size). This is would allow, among other things, to fake struct inheritance to a degree - although I'm not sure if that degree is satisfactory to proponents of that mechanism. Limitations: - Interface instances can only point to a struct, not contain it, therefore when persistence of data outside the scope is required - heap allocation is still unavoidable - Interface casts are dynamic and therefore introduce overhead that might not appeal to people using structs for performance If the cast to an interface was static, runtime overhead would be less, but problems with scoping arise. Example: external interface mappings for a template argument would not be visible inside the template, unless it imported the external interface. Note that this feature would make casting an interface instance to Object virtually impossible (unless you're prepared for it to fail). I think it makes sense that an interface is a more abstract construct, and Object a more specific one. A "root" interface IAnything could be created that any interface or Object was castable to, where RTTI information would be kept in the vtable part of the interface. Every type that has an interace mapping would implicitly need to implement this interface. I'm not sure about the practicality of this solution, maybe it could be somehow united with TypeInfos. There surely is merit in wanting to have a root type that both objects and interfaces can be cast to. === Anyway, I'll just leave this here. It's not a killer feature, but since it seems possible to implement it, I thought I'd write it up. In the future concept maps could mimic this mechanism at compile time.
Oct 17 2008
"Hxal" wrote=========================== External interfaces =============================== The idea is to allow implementing an interface for a class, outside the class' definition. This is possible as I understand, because interfaces are "fat" pointers -- they consist of an object reference and the interfaces' vtable for the particular class, which is separate from the class' vtable. Therefore it should be possible to create an interface vtable outside the module that declares the class.I am not sure, but I don't think interfaces are 'fat' pointers. I think they are simply pointers. And the pointer points to a vtable in a class instance. The vtable's has an element that is an offset to subtract from the pointer to get to the real object (which is a constant value). So to make an external interface, you need to include the interface in the class data. At least in order to fit into the implementation of the current system. I could also be totally wrong ;) -Steve
Oct 17 2008
Hxal wrote:=========================== External interfaces =============================== The idea is to allow implementing an interface for a class, outside the class' definition. This is possible as I understand, because interfaces are "fat" pointers -- they consist of an object reference and the interfaces' vtable for the particular class, which is separate from the class' vtable. Therefore it should be possible to create an interface vtable outside the module that declares the class.[snip] It's a nice idea. I asked Walter about it today. Interfaces are not fat pointers, but your scheme could be implemented in other ways (in essence allocating vtables dynamically). This would be a sizeable implementation effort. The question is, to what extent would post-factum interfaces enable or improve idioms? Also, it might be of interest to you that I implemented compound types as described by Buchi and Weng, see https://eprints.kfupm.edu.sa/31204/1/31204.pdf), and also straight structural casts. These should cover at least a few idioms that could use post-factum interfaces. I haven't released the code to public attention because I haven't gotten around to it. Andrei
Oct 17 2008
Andrei Alexandrescu Wrote:The question is, to what extent would post-factum interfaces enable or improve idioms?I was really looking at the possibility of adapting objects to interfaces without additional wrapper allocation, but with dynamically allocated vtables that makes little sense.Also, it might be of interest to you that I implemented compound types as described by Buchi and Weng, see https://eprints.kfupm.edu.sa/31204/1/31204.pdf), and also straight structural casts. These should cover at least a few idioms that could use post-factum interfaces. I haven't released the code to public attention because I haven't gotten around to it.Being able to declare void foo (IReadable & ISeekable device) sounds wicked cool. But I don't have interface heavy code myself, someone who does would need to judge if it can simplify code in practice.
Oct 18 2008
Hxal wrote:=========================== External interfaces =============================== The idea is to allow implementing an interface for a class, outside the class' definition. This is possible as I understand, because interfaces are "fat" pointers -- they consist of an object reference and the interfaces' vtable for the particular class, which is separate from the class' vtable. Therefore it should be possible to create an interface vtable outside the module that declares the class. This would allow us to adapt classes to interfaces from different libraries. Example: | import foreign.io : ForeignFile = File; | | import my.io : IFile; | | interface IFile for ForeignFile// I like the use of "for" :)| { | void open (args) | { | object.FancyNameForOpen (args); | // typeof(this) == IFile | // typeof(object) == ForeignFile | // (cast(IFile) object) == this | // I've used the name 'object', but that's really arbitrary, | // it's just important that this variable was defined by the compiler | // so that every method invocation didn't perform a dynamic castsuper.FancyNameForOpen(args); //?| } | | /* et cetera */ | } | | void example () | { | IFile file = cast(IFile) new ForeignFile (); | }But I wonder if you can do definition like this with a pure interface. I mean, is it possible to write a new function like: interface IFile for ForeignFile { // suppose it is in IFile but can only be pretended in ForeignFile ulong fileLength (string filename) { object.open(filename); ulong len = 0; while (object.readByte()) ++ len; object.close(); return len; } // this contains no states void touch (string filename) { object.open(filename); object.close(); } } ? But then this is just equivalent to a wrapper class. I guess only existing functions can be mapped?Since supposedly D object/interface casts are always dynamic, there should be no problem with casting even if the cast site doesn't directly see the external interface declaration at compile time. External interfaces should be equivalent to regular interface implementations in respect to the inheritance hierarchy, and should be freely intermixable. AFAIK it's possible to implement, because the mapping of implemented interfaces can be built during runtime initialization. Potential problems that need to be solved include: - Two external interface definitions for the same interface and class pair in different modules; it'd be nice if the compiler could detect that, but I'm not sure if it's at all possible Advantages compared to wrapper classes: - No additional objects need to be allocated Disadvantages compared to wrapper classes: - No additional state can be kept ======================== Interfaces for other types =========================== The same mechanism could be used to implement interfaces for pointers to arbitrary types (not for value types themselves, as the object field of the interface instance is restricted to pointer size). This is would allow, among other things, to fake struct inheritance to a degree - although I'm not sure if that degree is satisfactory to proponents of that mechanism. Limitations: - Interface instances can only point to a struct, not contain it, therefore when persistence of data outside the scope is required - heap allocation is still unavoidable - Interface casts are dynamic and therefore introduce overhead that might not appeal to people using structs for performance If the cast to an interface was static, runtime overhead would be less, but problems with scoping arise. Example: external interface mappings for a template argument would not be visible inside the template, unless it imported the external interface. Note that this feature would make casting an interface instance to Object virtually impossible (unless you're prepared for it to fail). I think it makes sense that an interface is a more abstract construct, and Object a more specific one. A "root" interface IAnything could be created that any interface or Object was castable to, where RTTI information would be kept in the vtable part of the interface. Every type that has an interace mapping would implicitly need to implement this interface. I'm not sure about the practicality of this solution, maybe it could be somehow united with TypeInfos. There surely is merit in wanting to have a root type that both objects and interfaces can be cast to. === Anyway, I'll just leave this here. It's not a killer feature, but since it seems possible to implement it, I thought I'd write it up. In the future concept maps could mimic this mechanism at compile time.
Oct 18 2008
KennyTM~ Wrote:? But then this is just equivalent to a wrapper class. I guess only existing functions can be mapped?The whole proposal is moot in light of interfaces in D using a single pointer, because as Andrei said vtables would need to be allocated dynamically. That means it would be no different from wrapper classes, because heap allocation would still happen. :(
Oct 18 2008
This reminds me of an old proposal of mine (to which no one replied). <http://digitalmars.com/d/archives/digitalmars/D/Idea_Class_extension_as_an_interface_73127.html> Quoting myself:I'd like to propose a class extension that allows overriding members. What I'm proposing is that: 1. you can create a class extension for a given class you want to extend, such an extension containing member function definitions which would become available to any user of that class if he has included the extension's module; 2. functions definitions in the class extension can only access the public-accessible members of the extended class, package-accessible members if they're in the same package, or private-accessible memebers if they're in the same module; 3. extensions implicitly create an interface which can be reimplemented in a derivative of the extended class if the need arise to override some of the extension functions. Calling a non-final extension method would enquire the following process: check if the class implements the given extension interface; if it does: call from there; if it does not: call the base extension method. Making an extension method final means that the function cannot be reimplemented by subclasses, allowing us to bypass this process. Here's a simple example: ~~~~~~ class A { string exp; string toExpression() { return exp; } } // Extension interface for A, providing functions to be used with any A descendent extension Ext : A { string toQuotedExpression() { return quote(toExpression()); } } // Class B derives from A and reimplements Ext more efficiently. class B : A, Ext { string quotedExp; override string toQuotedExpression() { return quotedExp; } } ~~~~~~ This way, A's implemententor doesn't need to know about any extension interfaces applying to A, and A's subclasser can still override any extension he wishes, if he has access to it and it makes sense. Perhaps such extensions could also be defined for structs, typedefs or primitive types. In those cases however, functions cannot be overriden.-- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 18 2008