www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Formal optional interfaces

reply Jacob Carlborg <doob me.com> writes:
The next Objective-C integration feature that will be implemented will 
probably be support for "protocols" (this is what Objective-C calls 
"interfaces"). Objective-C protocols supports optional methods. To call 
an optional method on a Objective-C protocols you're supposed to do a 
runtime check to see if the object actually implements the method.

This got me thinking if this needs to be supported as well. Regular D 
interfaces do not support optional methods. I asked on Slack how people 
would think about this feature and I got a reply mentioning Andrei's C++ 
talk "The Next Big Thing" [1]. This talk includes informal optional 
interfaces.

I see a couple of problems with informal optional and non-optional 
interfaces:

1. Since there is no name in the code it's difficult to talk about. With 
a formal interface you can say "This type need to implement Numeric" 
while with an informal interface it would be more like "This type need 
to implement addition, subtraction, multiplication, division, comparison 
and a bunch of more things".

2. It's difficult to document an informal interface since there's no 
obvious symbol to attach the Ddoc comment to.

3. With optional informal interfaces there's the issue with misspelling 
or accidentally using the wrong name. If you misspell the name your 
function is not called and you don't know why. There are at least three 
different places where a symbol can be misspelled:

A. When implementing the code that requires the informal optional 
interface, this unlikely but can happen. Perhaps a specification says to 
implement a symbol named "color", which the developer reads, five 
minutes later the developer implement the symbol and names it "colour" 
instead.

B. When the documentation is written.

C. When implementing the informal optional interface.

Then I was thinking if of way to have formal optional interfaces, that 
would both work with integrating Objective-C protocols and a more D way 
of doing this at compile time. Which also would solve the above problems.

Here's the idea I have for formal optional interfaces:

* Implement a compiler recognized UDA to indicate that a method is 
optional in an interface:

interface Foo
{
     void foo(); // required
      optional void bar();
}

* In addition to classes, allow structs to implement interfaces. This 
would not mean that a struct can be passed to a function taking said 
interface or assigned to a variable of the implemented interface. It 
would be used for template constraints:

interface Foo
{
     void foo();
}

struct Bar : Foo
{
     void foo() { }
}

If Bar doesn't define a method named "foo" it would be a compile time error.

Use with a template:

void processFoo(T : Foo)(T foo)
{
}

processFoo(Bar()); // the type of T will be "Bar"

This works today with classes.

* Implement a compiler recognized UDA to indicate that an optional 
method is being implemented instead of declaring a new method. This is 
required to avoid the misspelling:

interface Foo
{
      optional void foo();
}

struct Bar : Foo
{
      implements void foo() { }
}

If " implements" is not specified a compile time error would occur. This 
is the same behavior as the "override" keyword (possibly "override" 
could be used here). This would check the signatures to make sure 
Foo.foo and Bar.foo matches.

* Implement a __traits to check if an optional method is implemented. It 
takes a type and a method signature:

interface Foo
{
      optional void foo();
}

struct Bar : Foo
{
      implements void foo() { }
}

void processFoo(T : Foo)(T foo)
{
     static if (__traits(implements, T, Foo.foo))
         foo.foo();
}

The "implements" trait would check not just the name but the full 
signature. It's important that the second parameter is not a string but 
the actual method to avoid any misspellings:

void processFoo(T : Foo)(T foo)
{
     static if (__traits(implements, T, Foo.fo))
         foo.foo();
}

The above example would give a compile time error because the method 
"fo" doesn't existing on the interface "Foo".

If a method is not checked with a "static if" to see if it's implemented 
or not a compile time error will occur if it's not implemented. If it is 
implement no error will occur. This is what we have today.

void processFoo(T : Foo)(T foo)
{
     foo.foo(); // compiles successfully
}

Back to the Objective-C integration. For an Objective-C interface we can 
use the same syntax:

extern (Objective-C) interface Foo
{
     void foo();
      optional void bar();
}

extern (Objective-C) class Bar : Foo
{
     void foo() {}
}

But when calling an optional method that is not implemented no compile 
time error will occur:

void main()
{
     Foo a = new Bar;
     a.bar(); // compiles successfully
}

Instead a runtime exception would be thrown from the Objective-C 
runtime. This is how Objective-C works. It's possible to dynamically add 
methods to Objective-C classes. The correct way is to do a runtime check:

void main()
{
     Foo a = new Bar;

     if (a.respondsToSelector("bar"))
         a.bar();
}

Thoughts?

[1] https://www.youtube.com/watch?v=tcyb1lpEHm0

-- 
/Jacob Carlborg
Mar 05
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 5 March 2019 at 20:03:30 UTC, Jacob Carlborg wrote:
 [snip]

 * Implement a __traits to check if an optional method is 
 implemented. It takes a type and a method signature:

 interface Foo
 {
      optional void foo();
 }

 struct Bar : Foo
 {
      implements void foo() { }
 }
 [snip]
What about with free-standing functions? impl void foo(Bar x) { } or maybe would need to do impl!Foo void foo(Bar x) { } This reminds me of open methods [1] [1] https://dlang.org/blog/2017/08/28/open-methods-from-c-to-d/
Mar 05
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 5 March 2019 at 20:03:30 UTC, Jacob Carlborg wrote:
 The next Objective-C integration feature that will be 
 implemented will probably be support for "protocols" (this is 
 what Objective-C calls "interfaces"). Objective-C protocols 
 supports optional methods. To call an optional method on a 
 Objective-C protocols you're supposed to do a runtime check to 
 see if the object actually implements the method.

 This got me thinking if this needs to be supported as well. 
 Regular D interfaces do not support optional methods. I asked 
 on Slack how people would think about this feature and I got a 
 reply mentioning Andrei's C++ talk "The Next Big Thing" [1]. 
 This talk includes informal optional interfaces.

 I see a couple of problems with informal optional and 
 non-optional interfaces:

 [...]
Atila Neves's `concepts` library on dub [1] already addresses most of these issues, without requiring new language features or invasive changes to existing code. What you propose might have been a good idea if it were part of D from the start (or if you were designing "D 3.0"), but trying to retrofit it now, after so much code (e.g., all of Phobos) has already been written using the existing "informal" style, seems like the worst of both worlds to me. It won't save us from having to deal with the shortcomings of "informal" interfaces, because code that uses them will still exist, but it *will* make the language more complicated and difficult to learn, and force programmers to waste their time dealing with incompatibilities between "formal" and "informal" code instead of solving actual problems. [1] https://code.dlang.org/packages/concepts
Mar 05