View mode: basic / threaded / horizontal-split · Log in · Help
October 17, 2008
"External interfaces" or "Interface maps"
=========================== 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.
October 17, 2008
Re: "External interfaces" or "Interface maps"
"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
October 18, 2008
Re: "External interfaces" or "Interface maps"
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
October 18, 2008
Re: "External interfaces" or "Interface maps"
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.
>
October 18, 2008
Re: "External interfaces" or "Interface maps"
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. :(
October 18, 2008
Re: "External interfaces" or "Interface maps"
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.
October 18, 2008
Re: "External interfaces" or "Interface maps"
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/
Top | Discussion index | About this forum | D home