www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Ducks

reply "Chris Williams" <yoreanon-chrisw yahoo.co.jp> writes:
I found some old threads about the concept of "static interface":

http://forum.dlang.org/thread/20091116172513.GB13817 llucax.com.ar

I agree that using template checks to validate compilability is 
more flexible than interface definitions, specifically because 
you can create cross-compatibility between functions and 
properties or allow for undefined types. However, I think that 
template checks have two large flaws:

1. You only know what the target is that you have to hit by 
looking at their source. Or, the person who wrote the template 
checker needs to, effectively, rewrite their check as a document 
comment. (E.g. see std.range isInputRange)

2. Writing functions which are restricted to a particular type is 
long-winded and subsequently error-prone. (E.g. all 
implementations of std.random uniform() should check 
isUniformRNG(), when they accept an external generator)

I think that the advantages that are added by template-based 
compilability checks can be gained without losing flexibility if 
we add a more lenient interface definition, like:

duck InputRange {
   bool empty; // can be a function or not. Doesn't care. Anyone 
using it must avoid using () syntax
   auto front; // Doesn't care what the return type is so long as 
it's non-void
   void next; // Cannot return a value
}

While a "duck" would allow one to define methods with explicitly 
typed parameters and return types like:

duck IntStack {
    void push(int value); // must accept an int
    int pop(); // must be a method that returns an int
}

It's easy and preferred to define ducks with the greatest 
flexibility.

Since these would still be a feature of the templating system, I 
might recommend using them like:

void foo(Range : InputRange)(Range r) {
    ...
}

Rather than:

void foo(InputRange r) {
    ...
}

It's still a little bit verbose, but it would help people to 
notice that ducked parameters are available at compile-time only.

And since these would be an official part of the language, DDoc 
would generate a public definition of the type target, like it 
does with interfaces.

I would also suggest a further version of ducks, for things like 
this:

duck Integral if (is(Integral : int)); // not a particularly good 
check

Thus allowing code like:

void foo(Int : Integral)(Int i) {
    ...
}

These wouldn't be quite as self-documenting, but I think would 
allow the general collapse of type specialization into the type 
specialization field.
Feb 05 2014
next sibling parent "Francesco Cattoglio" <francesco.cattoglio gmail.com> writes:
On Thursday, 6 February 2014 at 01:01:59 UTC, Chris Williams 
wrote:
 I think that the advantages that are added by template-based 
 compilability checks can be gained without losing flexibility 
 if we add a more lenient interface definition, like:
I love the idea of some kind of "static interface" too. Pretty sure it would also allow better compile-time polymorphism. The problem I see with this proposal however is that the chances of getting it into the core language are zero. Perhaps one could get a library solution if what we are looking for is extra clarity. Something like void DoSomething(T)(T t) if (DuckTyping(T, MyType) { ... stuff ... }
Feb 06 2014
prev sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Thursday, 6 February 2014 at 01:01:59 UTC, Chris Williams 
wrote:
 1. You only know what the target is that you have to hit by 
 looking at their source. Or, the person who wrote the template 
 checker needs to, effectively, rewrite their check as a 
 document comment. (E.g. see std.range isInputRange)
The documentation must explain everything the concept entails. It is not acceptable that the user is forced to look at Phobos' source code to figure out a public interface. If that's forced anywhere in Phobos, it's a bug. While it's not hard to remember that an input range needs a boolean property `empty`, a property `front` of any non-void type and a void `popFront()` method, I agree that a more formalized solution of documenting concepts would be an improvement. The current method of replicating a documented version of the source code in the documentation is a bit icky, as the user has to *derive* how to write their types based on that code snippet.
 2. Writing functions which are restricted to a particular type 
 is long-winded and subsequently error-prone. (E.g. all 
 implementations of std.random uniform() should check 
 isUniformRNG(), when they accept an external generator)
Perhaps error prone, but in terms of conciseness there aren't that many differences: --- // Concepts (current approach) T uniform(T, RNG)(ref RNG rng) if (isUniformable!T && isUniformRNG!RNG); // Interfaces T uniform(T)(IUniformRNG rng) if (isUniformable!T); // Protocols T uniform(T : IUniformable, RNG : IUniformRNG)(ref RNG rng); --- It seems only the interface approach can claim being less error prone.
 I think that the advantages that are added by template-based 
 compilability checks can be gained without losing flexibility 
 if we add a more lenient interface definition, like:

 duck InputRange {
   bool empty; // can be a function or not. Doesn't care. Anyone 
 using it must avoid using () syntax
   auto front; // Doesn't care what the return type is so long 
 as it's non-void
   void next; // Cannot return a value
 }
It is usually called a "protocol".
Feb 06 2014
parent "Chris Williams" <yoreanon-chrisw yahoo.co.jp> writes:
On Thursday, 6 February 2014 at 10:31:45 UTC, Jakob Ovrum wrote:
 It seems only the interface approach can claim being less error 
 prone.
Making them interchangeable with Interfaces is certainly an option. It's easy to write and doesn't actually lose anything in the end result. I was figuring that proximity to the target would make people less likely to forget to specialize their templates and for people looking at the definition to figure out what limitations were being attached to what, but I'm an easy sell for short-to-type and easy-to-read.
Feb 06 2014