www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "External interfaces" or "Interface maps"

reply Hxal <Hxal freenode.irc> writes:
=========================== 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
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"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
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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.

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
parent Hxal <Hxal freenode.irc> writes:
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
prev sibling next sibling parent reply KennyTM~ <kennytm gmail.com> writes:
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 cast

super.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
parent Hxal <Hxal freenode.irc> writes:
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
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
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