www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - property magic for inerfaces (please!)

reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Consider the following hypothetical interface:

interface IPerson
{
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();
}

For practical purposes, it would be nice to have some methods defined in 
that interface, such as:

bool hasChildren()
{
     return getChildren().length != 0;
}

but it's not possible to define this method right in the interface. 
(Suppose say we really have to have the thing as an interface and not as 
an abstract class).

A possible solution is to add it to the interface without defining it,

interface IPerson
{
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();

     bool hasChildren();
}

while hoping that all classes implemeting this interface would define 
the method in the same way, i.e.
bool hasChildren()
{
     return getChildren().length != 0;
}
The main problem with this is that it's not practical more complicated 
functions.

Another solution is to define the function outside of the interface,

bool hasChildren(IPerson dude)
{
     return dude.getChildren().length != 0;
}

The problem with this is that it doesn't feel quiet right when you use it.
...
IPerson guy = .... //something
..
if( hasChildren(guy) )
{
  ....
}

It would be nice if the compiler would allow you to call this method as 
a property (like with arrays)

if( guy.hasChildren() )
{
  ....
}

Can we have this feature before v1.0?
Basically, a function that takes an interface as a first parameter can 
be considered a property of that interface.
Nov 18 2006
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Hasan Aljudy wrote:
 Consider the following hypothetical interface:
 
 interface IPerson
 {
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();
 }
 
 For practical purposes, it would be nice to have some methods defined in
 that interface, such as:
 
 bool hasChildren()
 {
     return getChildren().length != 0;
 }
 
 but it's not possible to define this method right in the interface.
 (Suppose say we really have to have the thing as an interface and not as
 an abstract class).
 
 A possible solution is to add it to the interface without defining it,
 
 interface IPerson
 {
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();
 
     bool hasChildren();
 }
 
 while hoping that all classes implemeting this interface would define
 the method in the same way, i.e.
 bool hasChildren()
 {
     return getChildren().length != 0;
 }
 ...

There are two ways of doing this, depending on the situation. The first is to have an abstract base class, which allows you to add partial implementation. The other is to provide a mixin for the "default" implementation of certain methods. For example:
 interface IPerson
 {
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();

     bool hasChildren();
 }

 template MPerson
 {
     bool hasChildren()
     {
         return this.getChildren.length != 0;
     }
 }

 class SomePerson : IPerson
 {
     mixin MPerson;

     // ...
 }

In this case, SomePerson needs to implement the first four methods, but hasChildren is implemented by the mixin. Ruby does something similar for implementing things like comparison operators, sorting, etc. Of course, extension methods would be *even cooler*, but this works right now :) -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
Nov 18 2006
parent reply Reiner Pope <reiner.pope gmail.REMOVE.com> writes:
== Quote from Daniel Keep (daniel.keep.lists gmail.com)'s article
 There are two ways of doing this, depending on the situation.
 The first is to have an abstract base class, which allows you to add
 partial implementation.
 The other is to provide a mixin for the "default" implementation of
 certain methods.

since they involve adding hasChildren to the vtbl, meaning it could have any implementation. What Hasan was asking for was the ability for interfaces to declare final methods in interfaces, which would mean they can't be overridden. To achieve this the "proper" way you either need to support final methods in interfaces or extension methods. Of course, the latter is already partly implemented, and is a much more powerful feature, so I think it should be added to D in any case.
Nov 18 2006
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Reiner Pope wrote:
 == Quote from Daniel Keep (daniel.keep.lists gmail.com)'s article
 There are two ways of doing this, depending on the situation.
 The first is to have an abstract base class, which allows you to add
 partial implementation.
 The other is to provide a mixin for the "default" implementation of
 certain methods.


That's a possible solution, but it's still adds the inconvenience of having to know which mixins go with the interface whenever you want to implement it.
 The problem with both of these solutions is that they are conceptually wrong,
 since they involve adding hasChildren to the vtbl, meaning it could have any
 implementation. What Hasan was asking for was the ability for interfaces to
 declare final methods in interfaces, which would mean they can't be overridden.
 
 To achieve this the "proper" way you either need to support final methods in
 interfaces or extension methods. Of course, the latter is already partly
 implemented, and is a much more powerful feature, so I think it should be
added to
 D in any case.

Maybe final methods would work better .. Imagine a class that implements IPerson class Kid : IPerson { ... } Without final methods, "Kid" won't really have access to the magic properties of IPerson. Kid k = ... ... if( k.hasChildren() ) //ops, no such property for class Kid { ... }
Nov 18 2006
prev sibling parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Hasan Aljudy" <hasan.aljudy gmail.com> wrote in message 
news:ejo4sm$h6$1 digitaldaemon.com...
 Consider the following hypothetical interface:

 interface IPerson
 {
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();
 }

 For practical purposes, it would be nice to have some methods defined in 
 that interface, such as:

 bool hasChildren()
 {
     return getChildren().length != 0;
 }

 but it's not possible to define this method right in the interface. 
 (Suppose say we really have to have the thing as an interface and not as 
 an abstract class).

 A possible solution is to add it to the interface without defining it,

 interface IPerson
 {
     IPerson getFather();
     IPerson getMother();
     IPerson[] getSiblings();
     IPerson[] getChildren();

     bool hasChildren();
 }

 while hoping that all classes implemeting this interface would define the 
 method in the same way, i.e.
 bool hasChildren()
 {
     return getChildren().length != 0;
 }
 The main problem with this is that it's not practical more complicated 
 functions.

What if getChildren() is a costly function, that, say, does an SELECT on a remote database? You would not want it to have that particular implementation since it would not need to collect the children first, just to check whether there are any. Unfortunately, the only correct way to work with interfaces *is* declaring all the functions virtual. You want to do getChildren().length because you already know the implementation of getChildren, which is not a fair way to look at it. L.
Nov 18 2006
parent reply Reiner Pope <reiner.pope gmail.REMOVE.com> writes:
== Quote from Lionello Lunesu (lionello lunesu.remove.com)'s article
 What if getChildren() is a costly function, that, say, does an SELECT on a
 remote database? You would not want it to have that particular
 implementation since it would not need to collect the children first, just
 to check whether there are any.
 Unfortunately, the only correct way to work with interfaces *is* declaring
 all the functions virtual. You want to do getChildren().length because you
 already know the implementation of getChildren, which is not a fair way to
 look at it.
 L.

But you could apply this sort of argument ("we want to allow people to implement it differently") everywhere so, by that logic, we shouldn't have final methods, since there *might* sometime be a reason to reimplement them (and you could even extend this argument to disallow every function which isn't a class, since none of them involve a virtual function lookup!). There is a limit to how much flexibility can be achieved in an API, and it is sometimes useful to final methods. If the interface expects that getChildren will run in a reasonable time, then it violates the Liskov Substitution principle if an implementing class takes too long on it. Cheers, Reiner
Nov 19 2006
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Reiner Pope wrote:
 == Quote from Lionello Lunesu (lionello lunesu.remove.com)'s article
 What if getChildren() is a costly function, that, say, does an SELECT on a
 remote database? You would not want it to have that particular
 implementation since it would not need to collect the children first, just
 to check whether there are any.
 Unfortunately, the only correct way to work with interfaces *is* declaring
 all the functions virtual. You want to do getChildren().length because you
 already know the implementation of getChildren, which is not a fair way to
 look at it.
 L.

But you could apply this sort of argument ("we want to allow people to implement it differently") everywhere so, by that logic, we shouldn't have final methods, since there *might* sometime be a reason to reimplement them (and you could even extend this argument to disallow every function which isn't a class, since none of them involve a virtual function lookup!). There is a limit to how much flexibility can be achieved in an API, and it is sometimes useful to final methods. If the interface expects that getChildren will run in a reasonable time, then it violates the Liskov Substitution principle if an implementing class takes too long on it. Cheers, Reiner

Yeah, and it's not like the interface is sooo abstract that it doesn't define anything at all! It does define something; it defines "what" should be done.
Nov 19 2006
prev sibling parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Sun, 19 Nov 2006 13:27:14 +0200, Reiner Pope  =

<reiner.pope gmail.REMOVE.com> wrote:

 =3D=3D Quote from Lionello Lunesu (lionello lunesu.remove.com)'s artic=

 What if getChildren() is a costly function, that, say, does an SELECT=


 on a
 remote database? You would not want it to have that particular
 implementation since it would not need to collect the children first,=


 just
 to check whether there are any.
 Unfortunately, the only correct way to work with interfaces *is*  =


 declaring
 all the functions virtual. You want to do getChildren().length becaus=


 you
 already know the implementation of getChildren, which is not a fair w=


 to
 look at it.
 L.

But you could apply this sort of argument ("we want to allow people to=

 implement
 it differently") everywhere so, by that logic, we shouldn't have final=

 methods,
 since there *might* sometime be a reason to reimplement them (and you =

 could even
 extend this argument to disallow every function which isn't a class,  =

 since none of
 them involve a virtual function lookup!). There is a limit to how much=

 flexibility
 can be achieved in an API, and it is sometimes useful to final methods=

 If the
 interface expects that getChildren will run in a reasonable time, then=

 it violates
 the Liskov Substitution principle if an implementing class takes too  =

 long on it.

 Cheers,

 Reiner

Well said. But why should functions defined in an interface to be final? They could= = be 'copied' to a class creating virtual functions just like mixins do. This way you could use interfaces to create final functions and default = = implemenations for virtual functions. No need to split an interface to a= = mixin + interface definitions.
Nov 19 2006