2013-05-16 13 views
6

私はDが新しく、コンパイル時にチェックされたダックタイピングが便利かどうかは疑問でした。Duck typing in D

たとえば、一連のメソッドを定義し、それらのメソッドを関数に渡す型に対して定義する必要があります。これは、D型のinterfaceと少し異なります。なぜなら、「型XはインタフェースYを実装する」と宣言する必要がないからです。メソッドが見つかるか、コンパイルが失敗します。また、これが構造体やクラスだけでなく、どのような型でもできるようにするのは良いことです。私は見つけることができる唯一のリソースは、以下のアプローチがこれを行うにはまともな方法だろうことを示唆している、this email threadた:

void process(T)(T s) 
    if(__traits(hasMember, T, "shittyNameThatProbablyGetsRefactored")) 
    // and presumably something to check the signature of that method 
{ 
    writeln("normal processing"); 
} 

...と、次のようにライブラリコールを実装にそれを作ることができることを示唆しています

struct Interface { 
    bool foo(int, float); 
    static void boo(float); 
    ... 
} 

static assert (Implements!(S, Interface)); 
struct S { 
    bool foo(int i, float f) { ... } 
    static void boo(float f) { ... } 
    ... 
} 

void process(T)(T s) if (Implements!(T, Interface)) { ... } 

クラスまたは構造体で定義されていない関数に対してこれを行うことは可能ですか?それを行うための他の/新しい方法はありますか?同様のことが行われていますか?

明らかに、この制約のセットは、Goのタイプのシステムに似ています。私は炎戦を始めようとはしていません - 私はGoがうまくいくようにDを使用しています。

+0

すべてのユースケースを処理する単一の機能を作成しようとしていますか?私はこれには[静的foreach](http://d.puremagic.com/issues/show_bug.cgi?id=4085)が必要だと思いますが、わかりません。たぶん、CTFEの魔法が使えるのでしょうか?また興味があります:http://www.digitalmars.com/d/archives/digitalmars/D/static_foreach_108369.html – tjameson

+0

@tjamesonはい、上記の例では、あなたが 'Interface 'struct at compile time。しかし、同じ目標を達成するための他の方法もあります。 – Dan

+0

Dにはラップアンドラップがあります(http://dlang.org/phobos-prerelease/std_typecons.html#.wrapとhttp://dlang.org/phobos-prerelease/std_typecons.html#.unwrap)アヒルタイピング。 – DejanLekic

答えて

7

これは実際にDで行うのが非常に一般的なことです。範囲の仕組みです。たとえば、範囲の最も基本的なタイプ - 入力範囲は - 3つの機能を持っている必要があります。

bool empty(); //Whether the range is empty 
T front(); // Get the first element in the range 
void popFront(); //pop the first element off of the range 

を鋳型の機能が、その後のタイプが有効な範囲であるかどうかをチェックするためにstd.range.isInputRangeを使用しています。例えば、std.algorithm.findの最も基本的な過負荷が

R find(alias pred = "a == b", R, E)(R haystack, E needle) 
if (isInputRange!R && 
    is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) 
{ ... } 

isInputRange!Rのように見えるRが有効な入力の範囲で、かつpredhaystack.frontneedleを受け入れ、boolに暗黙的に変換されるタイプを返す場合is(typeof(binaryFun!pred(haystack.front, needle)) : bool)trueある場合trueです。したがって、このオーバーロードは完全にスタティックダックタイピングに基づいています。

isInputRange自体に関しては、それは

template isInputRange(R) 
{ 
    enum bool isInputRange = is(typeof(
    { 
     R r = void;  // can define a range object 
     if (r.empty) {} // can test for empty 
     r.popFront();  // can invoke popFront() 
     auto h = r.front; // can get the front of the range 
    })); 
} 

のようになりますこれは、同名のテンプレートですので、それが使われているとき、それはこの場合にはタイプboolの列挙型で、その名前と記号に置き換えられます。式のタイプがvoidの場合、booltrueです。式が無効な場合、typeof(x)の結果はvoidになります。それ以外の場合は、式xの型です。 is(y)の結果は、yの場合はvoidとなります。したがってisInputRangetypeof式のコードがコンパイルされた場合はtrueになり、それ以外の場合はfalseとなります。

isInputRange中での発現は、あなたがRRの機能を有していることが条件で使用することができemptyという名前のメンバーを(それは関数、変数、または何も)持っていることを、タイプRの変数を宣言できることを確認引数をとらないpopFrontという名前で、Rには値を返すメンバfrontがあります。これは入力範囲で予想されるAPIで、RがそのAPIに従うとtypeofの式がコンパイルされるため、そのタイプのisInputRangetrueになります。それ以外の場合は、falseになります。

Dの標準ライブラリには、このようなシノニムテンプレート(典型的には特性と呼ばれる)がかなりあり、テンプレートの制約条件でそれらを頻繁に使用します。特にstd.traitsにはかなりの数があります。だから、そのような特質がどのように書かれているのかというもっと多くの事例が必要な場合は、そこを見ることができます(しかし、そのうちのいくつかはかなり複雑です)。そのような特性の内部は、特にきれいではありませんが、テンプレートの制約がきれいで分かりやすくなるように、ダックタイピングテストをうまくカプセル化します(これらのテストが直接挿入されれば、はるかに醜いでしょう)。

これは、Dでの静的なダックタイピングの通常の方法です。よく書いていく方法を理解するのは少し練習しますが、これは標準的な方法です。あなたのImplements!(S, Interface)の提案に似たものを思いつくことを提案してきた人がいますが、実際にはまだ何も起こっていませんし、そのようなアプローチは実際には柔軟性が低く、多くの特性それは確かに基本的なもので動作するようにすることができます)。それにもかかわらず、私がここで説明したアプローチは現在、それを行う標準的な方法です。

また、範囲についてよくわからない場合は、thisとお伝えください。

+0

実際には、Dのメタプログラミングに関する最も単純で不便なことの一つです。 "Implements"(私の答えを見てください)を実装するのに問題はありませんが、本当に便利であるためには、ネイティブ構文テンプレートtmpl(T:DuckTypeInterface)。もう少し柔軟なisXRangeですが、はるかに読みやすく、エラーメッセージもずっと優れています。我々はすぐにそれのような何かを期待できるとは思わない。 –

2

実装していますが、(S、Interface)は可能ですが、標準ライブラリに入るか、言語サポートを強化するために十分注意を払っていませんでした。おそらく、私はそれがダックタイピングのために行くための方法です伝えるだけではありませんならば、我々はそれを持っている:)コンセプト実装の

証明を周りいじくり回すチャンスがあります。

http://dpaste.1azy.net/6d8f2dc4

import std.traits; 

bool Implements(T, Interface)() 
    if (is(Interface == interface)) 
{ 
    foreach (method; __traits(allMembers, Interface)) 
    { 
     foreach (compareTo; MemberFunctionsTuple!(Interface, method)) 
     { 
      bool found = false; 

      static if (!hasMember!(T, method)) 
      { 
       pragma(msg, T, " has no member ", method); 
       return false; 
      } 
      else 
      {    
       foreach (compareWhat; __traits(getOverloads, T, method)) 
       { 
        if (is(typeof(compareTo) == typeof(compareWhat))) 
        { 
         found = true; 
         break; 
        } 
       } 

       if (!found) 
       { 
        return false; 
       } 
      } 
     } 
    } 
    return true; 
} 

interface Test 
{ 
    bool foo(int, double); 
    void boo(); 
} 

struct Tested 
{ 
    bool foo(int, double); 
// void boo(); 
} 

pragma(msg, Implements!(Tested, Test)()); 

void main() 
{ 
}