ダック・タイピング
このページは著作権侵害のおそれが指摘されており、事実関係の調査が依頼されています。
このページの現在または過去の版は、ウェブサイトや書籍などの著作物からの無断転載を含んでいるおそれが指摘されています。もしあなたが転載元などをご存知なら、どうぞこのページのノートまでご一報ください。 著作権侵害が確認されると、このページは削除の方針により一部の版または全体が削除されます。もしこのページの加筆や二次利用をお考えでしたら、この点を十分にご認識ください。 |
ダック・タイピング(英: duck typing)とは、Smalltalk、Perl、PHP、Python、Ruby、JavaScriptなどの動的型付けに対応したオブジェクト指向プログラミング言語に特徴的な、型付けのスタイル(作法)のひとつである。ダック・タイピングはポリモーフィズム(多態性)を実現する手段のひとつとして使われる[1]。
Pythonのリファレンスでは、ダック・タイピングは「あるオブジェクトが正しいインタフェースを持っているかどうかを決定するために、オブジェクトの型を見ることはしないプログラミングスタイルである」と説明されている。代わりに、オブジェクトが持つメソッドや属性(フィールドまたはプロパティ)が単純に呼ばれたり使われたりする。特定の型よりもインタフェースを重視することで、うまく設計されたコードは、ポリモーフィックな代入の許可による柔軟性を向上する[2]。
概要
編集静的型付け言語では型検査をコンパイル時に実施する一方、動的型付け言語では型検査を実行時に実施する[3]。つまり、動的型付けでは、オブジェクト(変数の値)に何ができるか(どのようなインタフェースを持っているか)は、実行時のオブジェクトそのものが決定する。
ダック・タイピングについて、継承機能によるサブタイプ(部分型)多相 (subtype polymorphism)[4] を主に用いる静的型付けのオブジェクト指向言語であるJavaやC#の概念で例えると、オブジェクトがあるインタフェースのすべてのメソッドを持っているならば、たとえそのクラスがそのインタフェースを宣言的に実装していなくとも、オブジェクトはそのインタフェースを実行時に実装しているとみなせる、ということである。それはまた、同じインタフェースを実装するオブジェクト同士が、それぞれがどのような継承階層を持っているのかということと無関係に、相互に交換可能であるという意味でもある。ダック・タイピングは構造的型付け (structural typing) に類似しており、構造的型付けの多態性(多相性)はロー多相 (row polymorphism) に分類されることがある[5](日本語では行多相[6]あるいは列多相[7]とも)。構造的型付けは名前的型付け (nominal typing / nominative typing) [注釈 1][8]と対比される型システムの分類のひとつである。
この用語の名前は「ダック・テスト」に由来する。
"If it walks like a duck and quacks like a duck, it must be a duck"
(もしもそれがアヒルのように歩き、アヒルのように鳴くのなら、それはアヒルに違いない)
デーブ・トーマスはRubyコミュニティで初めてこの言葉を使ったと考えられている。[要出典]
C++のテンプレートはダック・タイピングの静的版である。例えば、STLにおける各種のiteratorはIterator基底クラスのようなものからメソッドを継承しているわけではないが、特定の演算子を利用できて同じ構文でコンパイルが通るならば、それはiteratorの一種として扱える。この文脈で言う「同じインタフェースを持つ」とは、コンパイラにとってシグネチャなどのインタフェースが同じだということである。したがって、iteratorの実装はオブジェクトである必要すらない。
もう1つ、ダック・タイピングに似たアプローチにOCamlの構造的サブタイピング (structural subtyping) がある[注釈 2]。メソッドのシグネチャが互換ならば、宣言上の継承関係はなくとも、オブジェクトの型は互換であるというものである。これはOCamlの型推論システムによってコンパイル時にすべて決定される(コンパイラによる静的な型検査を受ける)。TypeScriptの型互換性も構造的サブタイピングに基づいている[9]。対義語は名前的サブタイピング (nominal subtyping) であり、例えばJavaやC#などの継承ベースの型システムで用いられている方式のことである。
PythonやRubyは一般的なクラス継承の機能も持っており、継承によるポリモーフィズムも利用できるが、ダック・タイピングによるポリモーフィズムは継承が不要であり、型による制約に縛られることなく簡素なコードで実現できる点にメリットがある。しかし、制約がないということは乱用しやすいということの裏返しでもある。型制約がない場合はコンパイラによる静的な型検査や統合開発環境 (IDE) によるコード補完などの支援が見込めず、プログラムの記述ミスを発見するのが難しくなることもある。Pythonのダック・タイピングでは、オブジェクトが特定の属性を実際に持っているかどうかをhasattr()
で事前チェックしたり、例外処理を記述したりする方法が採られる[2]。Pythonはダック・タイピングを補完するために抽象基底クラスの機能を導入した[10]。
Rubyでの例
編集Rubyでの単純な例を示す。
def test(foo)
puts foo.sound
end
class Duck
def sound
'quack'
end
end
class Cat
def sound
'myaa'
end
end
test(Duck.new)
test(Cat.new)
出力は以下である。
quack myaa
2つのクラスに継承の関係が無いことに注目して欲しい。上記のtest
メソッドは、sound
という名前のメンバーを持つオブジェクトであれば何でも受け付ける。なお、Rubyのputs
は引数オブジェクトを自動的に文字列に変換して標準出力に出力する[11]ため、仮に文字列以外を返すsound
であっても受け付ける。このような柔軟性が動的言語の特徴である。
C++での例
編集上記Rubyの例をC++で記述すると、以下のようになる。
#include <iostream>
template <class T>
void test(const T& t) {
std::cout << t.sound() << std::endl;
}
struct Duck {
const char* sound() const {
return "quack";
}
};
struct Cat {
const char* sound() const {
return "myaa";
}
};
int main() {
test(Duck());
test(Cat());
}
実行結果はRubyの例と同じである。ただし、テンプレートによるダック・タイピングはコンパイル時に解決される静的なポリモーフィズム (static polymorphism) であり、動的型付け言語とは異なり実行時のオーバーヘッドを伴わない。
C#での例
編集C#はバージョン4.0で動的型付けを可能にするdynamic
型が使えるようになった。dynamic
は内部的にはリフレクションを利用して実装されており、該当するメソッドやプロパティの存在有無を実行時に遅延評価する。シンボル解決に失敗した場合は例外がスローされる[12]。
using System;
class Duck {
public string Sound() {
return "quack";
}
}
class Cat {
public string Sound() {
return "myaa";
}
}
public class DuckTypingTest {
static void Test(dynamic obj) {
Console.WriteLine(obj.Sound());
}
public static void Main() {
Test(new Duck());
Test(new Cat());
}
}
なお、C#はC++のテンプレートに似た機能としてジェネリクスをサポートするが、C++テンプレートほどの柔軟性はなく、ダック・タイピングに使用することはできない[13]。
VB.NETでの例
編集Visual Basic .NET (VB.NET) はOption Strict Off
が指定されている場合、System.Object
型による遅延バインディング (late binding) ができるようになり、これによりダック・タイピングをサポートできる[14][15]。これは、VB.NETが登場当初から従来のVisual BasicおよびVisual Basic for Applications (VBA) からの移行を狙っていたため、ゆるい型付けをサポートする必要があったことに由来する。
下記の例は、Option Strict On
を指定すると静的な型チェックが有効になり、コンパイルエラーになる。
Option Strict Off ' 遅延バインディングを許可する。
'Option Strict On ' 遅延バインディングを許可しない。
Imports System
Class Duck
Public Function Sound() As String
Return "quack"
End Function
End Class
Class Cat
Public Function Sound() As String
Return "myaa"
End Function
End Class
Public Class DuckTypingTest
Shared Sub Test(obj As Object)
Console.WriteLine(obj.Sound())
End Sub
Public Shared Sub Main()
Test(new Duck())
Test(new Cat())
End Sub
End Class
その他の言語など
編集Javaは言語構文レベルで動的型付けをサポートしないが、リフレクションを用いることで、ダック・タイピング相当を実現できる。Core Reflection APIとして、java.lang.reflect
パッケージが用意されている[16]。また、Java Native Interface (JNI) を用いることで、C言語やC++などのネイティブコードからJavaで書かれたクラスを利用することができるが、リフレクション同様にダック・タイピングに応用することもできる。
COMにおいても、IDispatch
インタフェース[17]を実装することで、ダック・タイピング相当を実現できる。VBScriptやJScriptといったスクリプト言語(動的プログラミング言語)の実装を容易にするための基盤として、拡張インタフェースIDispatchEx
も用意されている[18]。
脚注
編集注釈
編集出典
編集- ^ “Python Duck Typing - Example” (英語). Techie Hours (2020年6月28日). 2020年7月26日閲覧。
- ^ a b Python Software Foundation. “Glossary — Python 3.7.1 documentation, §duck-typing”. docs.python.org. 2018年11月8日閲覧。
- ^ Dynamic typing vs. static typing | Oracle
- ^ Benjamin C. Pierce「第15章 部分型付け」『型システム入門 −プログラミング言語と型の理論−』オーム社、2013年3月26日。ISBN 978-4274069116。
- ^ Objects and Aspects: Row Polymorphism | Neel Krishnaswami, Department of Computer Science, Carnegie Mellon University
- ^ 実例によるPureScript
- ^ OCamlで構築するモダンWeb:型付きHTML5プログラミングの実際 | 有限会社ITプランニング | 今井 敬吾
- ^ Benjamin C. Pierce「19.3 名前的型システムと構造的型システム」『型システム入門 −プログラミング言語と型の理論−』オーム社、2013年3月26日。ISBN 978-4274069116。
- ^ TypeScript: Documentation - Type Compatibility
- ^ Python Software Foundation. “Glossary — Python 3.10.1 documentation, §abstract base class”. docs.python.org. 2021年12月19日閲覧。
- ^ Kernel.#puts (Ruby 3.0.0 リファレンスマニュアル)
- ^ dynamic 型の使用 - C# プログラミング ガイド | Microsoft Docs
- ^ Differences Between C++ Templates and C# Generics - C# Programming Guide | Microsoft Docs
- ^ Object Variable Declaration - Visual Basic | Microsoft Docs
- ^ Early and Late Binding - Visual Basic | Microsoft Docs
- ^ Java Core Reflection
- ^ IDispatch Interface and Accessibility - Windows applications | Microsoft Docs
- ^ IDispatchEx Interface | Microsoft Docs
関連項目
編集- 型システム
- 動的型付け
- ポリモーフィズム
- 派生型
- ダイナミックバインディング
- 動的プログラミング言語
- ダック・テスト
- ジェネリックプログラミング
- en:Structural type system(構造的型システム)
- en:Nominal type system(名前的型システム)
- 型推論
外部リンク
編集この節に雑多な内容が羅列されています。 |
この節の出典は、Wikipedia:信頼できる情報源に合致していないおそれがあります。 |