型推論

型推論（かたすいろん、英: type inference）とはプログラミング言語の機能の1つで、静的な型付けを持つ言語において、変数や関数シグネチャの型を明示的に宣言しなくても、初期化のための代入式の右辺値や関数呼び出し時の実引数などといった、周辺情報および文脈などから自動的に（暗黙的に）各々の型を決定する機構のこと。
推論に失敗するとその時点でエラーを報告できるため、少なくとも誤った型を用いることによるバグは回避できる。また、アルゴリズムの記述に集中できるのでプログラムの抽象度が上がるというメリットもある。型名が長大な場合に、型推論による省略によってコード全体の見通しをよくすることにもつながるが、一方で統合開発環境による支援（コードエディター上のツールチップなど）が得られない環境では、一見して型が分からないことでコードレビューがしにくくなるというデメリットもある。
代表的な型推論アルゴリズムとして、Hindley/Milner 型推論アルゴリズムがある。各々著名なコンピュータ科学者の名前からつけられた名前であるが、Hindley は論理学者として型推論システムを先に開発した。
型推論を持つ言語としてはHaskell、ML、Vala、F#、C#、Java、Scala、OCaml、D言語、Concurrent Clean、C++ (C++11以降)、Swiftなどがある。静的型付け関数型プログラミング言語のほとんどがなんらかの型推論の機能を持っている。登場当初は型推論を持っていなかった言語であっても、関数型言語に影響を受けた拡張や改訂により型推論の機能を持つようになった言語も多い。
ただし型推論と関数の多重定義（オーバーロード）は相性が悪く、オーバーロードをサポートする言語では型推論による恩恵が十分に受けられない（型推論ではシグネチャを一意に決めることができない）ケースがある。

型推論のバリエーション

型推論により自動的に型を決定する機構は、変数宣言の際の暗黙的な型指定以外にも存在する。関数型言語ではほとんどの場面で型推論がサポートされるが、従来の手続き型言語やオブジェクト指向言語でのサポートは言語および各言語の規格バージョンによってまちまちである。

変数宣言時の型推論

C#はバージョン3.0にて、varキーワードを用いたローカル変数の宣言時型推論を導入した。制約のひとつとして、宣言時初期化を伴う必要がある。for文、foreach文、using文のスコープ変数にも利用できる。宣言時初期化文の右辺がメソッドグループや匿名関数（ラムダ式および匿名メソッド）の場合には適用できない。
// 型推論を用いない書き方。string s1 = "文字列";System.Console.WriteLine(s1.GetType()); // System.String// 型推論を用いた書き方。var s2 = "文字列";System.Console.WriteLine(s2.GetType()); // System.Stringvar now = System.DateTime.Now;System.Console.WriteLine(now.GetType()); // System.DateTimenow = s2; // コンパイルエラー。var action1 = () => {}; // コンパイルエラー。var action2 = delegate() {}; // コンパイルエラー。var action3 = System.GC.Collect; // コンパイルエラー。var action4 = new System.Action(() => {});System.Action action5 = () => {};var dict = new System.Collections.Generic.SortedDictionary<string, int> { {"Bravo", 0}, {"Alpha", 1}, {"Charlie", 2} };foreach (var entry in dict) {
    //System.Diagnostics.Debug.Assert(entry is System.Collections.Generic.KeyValuePair<string, int>);
    System.Console.WriteLine("Key={0}, Value={1}", entry.Key, entry.Value);}
この書き方はJavaScriptなど動的型付けの言語に非常によく似ているが、しかしながらすべての型はコンパイル時に定められる。また、バリアント型とは異なり、実行時に再代入によって変数の中身の型が変わるようなことはない。
ラムダ式の仮引数の型を省略した場合も型推論が働く。戻り値の型は常に型推論によって決定される。
// ラムダ仮引数の型推論を用いない書き方。System.Func<double, double> func1 = (double x) => x * x;// ラムダ仮引数の型推論を用いた書き方。System.Func<double, double> func2 = (x) => x * x;
Javaはバージョン8にてラムダ式を導入したが、C#同様に仮引数の型を省略すると型推論が働く。バージョン10にて、予約型名varによるローカル変数の宣言時型推論を導入した。バージョン11にて、ラムダ式の仮引数の型推論にもvarを使えるようになった。
C++はC++11規格にて、キーワードautoおよびdecltypeによる一部の変数の宣言時型推論を導入した。適用可能範囲はC#やJavaよりも広い。後継規格のC++14以降ではさらに適用可能範囲が広がっている。
namespace {
    auto g_variable = 0.0; // double
    struct MyType {
        static const auto s_variable = 0L; // long
    };}int main() {
    auto n = 0; // int
    decltype(n)* p1 = &n; // int*
    decltype(&n) p2 = &n; // int*
    decltype(n)& r1 = n; // int&
    decltype((n)) r2 = n; // int&
    auto f = []() {}; // コンパイラが生成する関数オブジェクト（クロージャ）型。}

戻り値の型推論

C++はC++11規格にて、キーワードautoおよびdecltypeによる戻り値の型推論を導入した。後継規格のC++14ではdecltype(auto)による簡略表現もサポートする。
#include <iostream>template<typename TFunc, typename TArg> auto invokeFunc(const TFunc& f, const TArg& a) -> decltype(f(a)) {
    return f(a);}int main() {
    std::cout << invokeFunc([](double x) { return x * x; }, 1.4142) << std::endl;}

総称型の型推論

例えば、Javaはバージョン5.0以降にてメソッドスコープの型変数を推論する機能を持つ。
// 型推論を用いない書き方。List<String> list1 = Collections.<String>emptyList();// 型変数へのバインドに型推論を用いた書き方。List<String> list2 = Collections.emptyList();
その他、Java 7 からは型変数を持つクラスをnewする場合にバインドすべき型を推論するダイヤモンド演算子という機能を持つ。
// 型推論を用いない書き方。List<String> list1 = new ArrayList<String>();// ダイヤモンド演算子による型推論。List<String> list2 = new ArrayList<>();

動的型言語における型推論

動的に型付けを行う言語の場合、文法的には型付けが行われず、あらゆる型の可能性を考慮して処理を進める必要があるため、処理が遅くなる原因となる。JITコンパイラによって高速化を図る場合、型推論によりあるデータを「特定の型」として扱うことが可能であれば、その型に合わせた処理だけをすることで高速化が行える。
JavaScript では、Webブラウザの分野では高速化が特に求められているため、2011年12月20日にリリースされたFirefox 9から高速化のため型推論技術を採用している。
Groovy 2.0 ではコンパイル時型検査 @TypeChecked の機能をつけたが、型を指定していない変数に対しても、型推論を利用して変数に型を割り振り、型検査を行っている。同様に Groovy に対応した IntelliJ IDEA などの統合開発環境も型推論を利用してアシストを行っている。