静的なアヒル型言語はありますか?
-
08-07-2019 - |
質問
この質問についてしばらくの間考えた後、静的アヒル型言語が実際に機能する可能性があることに気付きました。コンパイル時に定義済みのクラスをインターフェイスにバインドできないのはなぜですか?例:
public interface IMyInterface
{
public void MyMethod();
}
public class MyClass //Does not explicitly implement IMyInterface
{
public void MyMethod() //But contains a compatible method definition
{
Console.WriteLine("Hello, world!");
}
}
...
public void CallMyMethod(IMyInterface m)
{
m.MyMethod();
}
...
MyClass obj = new MyClass();
CallMyMethod(obj); // Automatically recognize that MyClass "fits"
// MyInterface, and force a type-cast.
そのような機能をサポートする言語を知っていますか? JavaまたはC#で役立ちますか?それは何らかの形で根本的に欠陥がありますか? MyClassをサブクラス化してインターフェイスを実装するか、アダプターデザインパターンを使用して同じことを達成できることを理解していますが、これらのアプローチは不要な定型コードのように見えます。
解決
静的に型付けされた言語は、定義により、実行時ではなく、コンパイル時に型をチェックします。上記のシステムに関する明らかな問題の1つは、実行時ではなくプログラムのコンパイル時にコンパイラが型をチェックすることです。
今では、プログラマに明示的に宣言型を持たせるのではなく、コンパイラにさらにインテリジェンスを組み込み、型を導出することができます。コンパイラは、 MyClass
が MyMethod()
メソッドを実装していることを確認し、それに応じてこのケースを処理できます。 (提案どおり)。このようなコンパイラは、 Hindley-Milner などの型推論を利用できます。
もちろん、Haskellのような静的に型付けされた言語の一部は、あなたが提案したものとすでに類似したことをしています。 Haskellコンパイラは(ほとんどの場合)型を明示的に宣言することなく型を推測できます。しかし、明らかに、Java / C#にはこの機能はありません。
他のヒント
この質問に対する新しい回答、 Goにはまさにこの機能があります。本当にクールだと思う賢い(実際の生活でどのように機能するか興味がありますが)考えてみてください。
公式ドキュメント(Tour of Goの一部、サンプルコード)に記載されているとおり:
インターフェースは暗黙的に実装されます
型は、メソッドを実装することによりインターフェースを実装します。がある 明示的な意図の宣言、「実装」はありません。キーワード。
暗黙的なインターフェースは、インターフェースの定義をそのインターフェースから分離します 実装は、パッケージなしで表示されます。 事前準備。
C ++でテンプレートを使用する方法はどうですか?
class IMyInterface // Inheritance from this is optional
{
public:
virtual void MyMethod() = 0;
}
class MyClass // Does not explicitly implement IMyInterface
{
public:
void MyMethod() // But contains a compatible method definition
{
std::cout << "Hello, world!" "\n";
}
}
template<typename MyInterface>
void CallMyMethod(MyInterface& m)
{
m.MyMethod(); // instantiation succeeds iff MyInterface has MyMethod
}
MyClass obj;
CallMyMethod(obj); // Automatically generate code with MyClass as
// MyInterface
このコードは実際にはコンパイルしていませんが、実行可能で、元の提案された(ただし機能しない)コードのC ++化は非常に簡単なものであると思います。
要点はわかりません。クラスがインターフェイスを実装し、それを実行したことを明示しないでください。インターフェースの実装は、このクラスがインターフェースが定義する方法で動作することになっていることを他のプログラマーに伝えます。メソッドに同じ名前と署名を付けるだけでは、設計者の意図がメソッドで同様のアクションを実行することであったという保証はありません。そうかもしれませんが、なぜそれを解釈(および誤用)のために残しておくのでしょうか?
「逃げる」ことができる理由動的言語でこれを成功させるには、言語自体よりもTDDの方が重要です。私の意見では、言語が、コードを使用/表示する他の人にこの種のガイダンスを提供する機能を提供する場合、それを使用する必要があります。それは実際に明瞭さを改善し、いくつかの余分な文字の価値があります。これを行うためのアクセス権がない場合、アダプターは、インターフェイスが他のクラスにどのように関連するかを明示的に宣言するという同じ目的を果たします。
F#は静的なカモタイピングをサポートしますが、キャッチでは:メンバー制約を使用する必要があります。詳細は、このブログエントリで静的に入力されたアヒル。
引用したブログの例:
let inline speak (a: ^a) =
let x = (^a : (member speak: unit -> string) (a))
printfn "It said: %s" x
let y = (^a : (member talk: unit -> string) (a))
printfn "Then it said %s" y
type duck() =
member x.speak() = "quack"
member x.talk() = "quackity quack"
type dog() =
member x.speak() = "woof"
member x.talk() = "arrrr"
let x = new duck()
let y = new dog()
speak x
speak y
MLファミリのほとんどの言語は、推論および制約された型スキームを備えた構造型をサポートしています。 -タイピング&quot;元の質問で。
このファミリーで最も人気のある言語には、Haskell、Objective Caml、F#、Scalaがあります。もちろん、あなたの例に最も近いものは、Objective Camlです。例の翻訳は次のとおりです。
open Printf
class type iMyInterface = object
method myMethod: unit
end
class myClass = object
method myMethod = printf "Hello, world!"
end
let callMyMethod: #iMyInterface -> unit = fun m -> m#myMethod
let myClass = new myClass
callMyMethod myClass
注:使用した名前の一部は、OCamlの識別子の大文字と小文字のセマンティクスの概念に準拠するために変更する必要がありますが、そうでなければ、これは非常に簡単な翻訳です。
また、注目に値するのは、 callMyMethod
関数の型注釈も iMyInterface
クラス型の定義も厳密に必要ではないことです。 Objective Camlは、型宣言をまったく行わなくても、例のすべてを推測できます。
Booは間違いなく静的なダック型言語です: http://boo.codehaus.org/Duck +入力
抜粋:
Booは静的に型付けされた言語です。 JavaやC#など。これはあなたのブーイングを意味します アプリケーションはほぼ同じ速度で実行されます 他の静的に型付けされたもの .NETまたはMonoの言語。しかし、使用して 静的に型付けされた言語 柔軟性のないものに制限し、 冗長なコーディングスタイル、 時には必要な型宣言 (&quot; x as int&quot;のようですが、これはそうではありません ブーのタイプのためにしばしば必要 推論)および時々必要 型キャスト(型のキャストを参照)。ブーズ 型推論のサポートと 最終的にはジェネリックがここで役立ちますが、...
あきらめることが適切な場合がある 静的によって提供されるセーフティネット タイピング。たぶんあなたは探検したいだけです あまり心配することなくAPI メソッドシグネチャまたは多分あなたは 外部と通信するコードを作成する COMオブジェクトなどのコンポーネント。どちらか 方法はあなたのものではないはずです 私のもの。
次のような通常の型とともに object、int、string ... booには 「アヒル」と呼ばれる特別なタイプ。用語 ルビープログラミングに触発されています 言語のアヒルタイピング機能(&quot; 鴨のように歩き、 鴨、それは鴨でなければなりません&quot;)。
C ++の新しいバージョンは、静的なカモタイピングの方向に移動します。いつか(今日?)このようなことを書くことができます:
auto plus(auto x, auto y){
return x+y;
}
そして、 x + y
に一致する関数呼び出しがない場合、コンパイルに失敗します。
あなたの批判に関して:
新しい&quot; CallMyMethod&quot;渡す型ごとに作成されるため、実際には型推論ではありません。
しかし、それは型推論であり( foo(bar)
と言うことができます。ここで foo
はテンプレート関数です)、より効率的である以外は同じ効果があります。コンパイルされたコードにより多くのスペースが必要です。
それ以外の場合、実行時にメソッドを検索する必要があります。名前を見つけてから、名前に正しいパラメーターを持つメソッドがあることを確認する必要があります。
または、一致するインターフェイスに関するすべての情報を保存し、インターフェイスに一致するすべてのクラスを調べて、そのインターフェイスを自動的に追加する必要があります。
どちらの場合でも、それにより暗黙的かつ誤ってクラス階層を壊すことができます。これは、C#/ Javaのプログラマーが慣れている習慣に反するため、新機能にとっては悪いことです。 C ++テンプレートを使用すると、すでに地雷原にいることがわかります(また、テンプレートパラメーターの制限を許可する機能(「概念」)も追加しています)。
Scalaの構造型はこのようなことを行います。
Visual Basic 9のプレリリースデザインは、動的インターフェイスを使用した静的なダックタイピングをサポートしていましたが、機能をカットしました * 時間どおりに出荷する。
D( http://dlang.org )は静的にコンパイルされた言語であり、wrap()によるダックタイピングを提供します。およびunwrap()( http://dlang.org/phobos-prerelease/std_typecons.html #.unwrap )。
私のプログラミング言語の最新バージョン Heron では、構造サブタイプ強制により類似したものをサポートしています。 as
という演算子。代わりに:
MyClass obj = new MyClass();
CallMyMethod(obj);
次のように記述します:
MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);
ちょうどあなたの例のように、この場合 MyClass
は明示的に IMyInterface
を実装する必要はありませんが、もしそうするとキャストは暗黙的に起こり、 asは
演算子は省略できます。
この記事。