(Translated by https://www.hiragana.jp/)
型推論 - Wikipedia

かた推論すいろん(かたすいろん、えい: type inference)とはプログラミング言語げんご機能きのうの1つで、静的せいてき型付かたつ言語げんごにおいて、変数へんすう関数かんすうシグネチャかた明示めいじてき宣言せんげんしなくても、変数へんすう宣言せんげんにおける初期しょきのための初期しょきや、関数かんすうしにおけるじつ引数ひきすうなどといった、周辺しゅうへん情報じょうほうおよび文脈ぶんみゃくなどから自動的じどうてきに(暗黙あんもくてきに)各々おのおのかた決定けっていする機構きこうのこと。言語げんごによってはtype deductionばれることもある。

推論すいろん失敗しっぱいするとその時点じてんでエラーを報告ほうこくできるため、すくなくともあやまったかたもちいることによるバグ回避かいひできる。また、アルゴリズム記述きじゅつ集中しゅうちゅうできるのでプログラムの抽象ちゅうしょうがるというメリットもある。型名かためい長大ちょうだい場合ばあいに、かた推論すいろんによる省略しょうりゃくによってコード全体ぜんたい見通みとおしをよくすることにもつながるが、一方いっぽう統合とうごう開発かいはつ環境かんきょうによる支援しえん(コードエディターじょうツールチップなど)がられない環境かんきょうでは、一見いっけんしてかたからないことでコードレビューがしにくくなるというデメリットもある。

代表だいひょうてきかた推論すいろんアルゴリズムとして、Hindley/Milner がた推論すいろんアルゴリズムがある。各々おのおの著名ちょめいなコンピュータ科学かがくしゃ名前なまえからつけられた名前なまえであるが、Hindley は論理ろんり学者がくしゃとしてかた推論すいろんシステムをさき開発かいはつした。

かた推論すいろん言語げんごとしてはHaskellMLValaOCamlF#C#JavaScalaC++D言語げんごConcurrent CleanSwiftなどがある。静的せいてき型付かたつ関数かんすうがたプログラミング言語げんごのほとんどがなんらかのかた推論すいろん機能きのうっている。登場とうじょう当初とうしょかた推論すいろんっていなかった言語げんごであっても、関数かんすうがた言語げんご影響えいきょうけた拡張かくちょう改訂かいていによりかた推論すいろん機能きのうつようになった言語げんごおおい。

ただしかた推論すいろん関数かんすう多重たじゅう定義ていぎ(オーバーロード)は相性あいしょうわるく、オーバーロードをサポートする言語げんごではかた推論すいろんによる恩恵おんけい十分じゅうぶんけられない(かた推論すいろんではシグネチャ一意いちいめることができない)ケースがある。

具体ぐたいれいによる説明せつめい

編集へんしゅう

このふしではかた推論すいろん構文こうぶん解析かいせき理論りろんにはんでいない。

ほとんどの言語げんごにおいては、関数かんすうかり引数ひきすうおよびもど演算えんざんオペランドおよび結果けっか変数へんすう、そしてそれらからしきは、各々おのおの保持ほじするデータの種類しゅるいあらわかたつ。構文こうぶんじょうあきらかな名前なまえかたによる区別くべつをしない言語げんごであっても、内部ないぶてきにはなんらかのかたっていて区別くべつしているケースがおおい。実行じっこうかたまる言語げんご動的どうてき型付かたつ言語げんごという。一方いっぽうコンパイルときかたまる言語げんご静的せいてき型付かたつ言語げんごという。静的せいてき型付かたつけの言語げんごにおいて、関数かんすうかり引数ひきすうおよびもどかた変数へんすうかたは、通常つうじょう明示めいじてき記述きじゅつする必要ひつようがある。たとえば、つぎC言語げんごれいである[1]

int addone(int x) {
    int result;
    result = x + 1;
    return result;
}

関数かんすう定義ていぎ最初さいしょくだりint addone(int x)では、関数かんすうaddone整数せいすうひとつを入力にゅうりょく引数ひきすうとしてり、整数せいすう出力しゅつりょく結果けっかとしてかえす、と宣言せんげんしている。int result;くだりでは、ローカル変数へんすうresult整数せいすうがたであることを宣言せんげんしている。

上記じょうきれいにほぼ1たい1で対応たいおうするコードを、F#使つかって記述きじゅつすると下記かきのようになる。

let addone (x : int) : int =
    let result : int = x + 1
    result

しかしF#はかた推論すいろん機能きのうっているため、つぎのようにくこともできる。

let addone x =
    let result = x + 1
    result

このF#のれいにおいて、

  • 2こう演算えんざん+ひだりオペランドとみぎオペランドのかたおなじであり、演算えんざん結果けっかおながたかえす。

という仕様しようであり、みぎオペランドには整数せいすうリテラル1記述きじゅつされていることから、ひだりオペランドの変数へんすうxすなわち関数かんすう引数ひきすうxおな整数せいすうがたであるということが推論すいろんされる。これにより、しきx + 1整数せいすうがたであることがかた推論すいろんされる。ゆえresultかた整数せいすうであり、addone関数かんすうもどかた整数せいすうであることがわかる。

let y1 = addone 3
let y2 = addone 3.0 // double がたわたすと、かた不一致ふいっちによりコンパイルエラー。
let y3 = addone 3y // sbyte がたわたすと、かた不一致ふいっちによりコンパイルエラー。

なお、かた推論すいろんはあくまで暗黙あんもく型付かたつけがなされるにすぎない。かた推論すいろんによりコンパイル確定かくていしたかた不変ふへんである。

かた推論すいろんのバリエーション

編集へんしゅう

かた推論すいろんにより自動的じどうてきかた決定けっていする機構きこうは、変数へんすう宣言せんげんさい暗黙あんもくてきかた指定してい以外いがいにも存在そんざいする。関数かんすうがた言語げんごではほとんどの場面ばめんかた推論すいろんがサポートされるが、従来じゅうらい手続てつづがた言語げんごオブジェクト指向しこう言語げんごでのサポートは言語げんごおよびかく言語げんご規格きかくバージョンによってまちまちである。

変数へんすう宣言せんげんかた推論すいろん

編集へんしゅう

C#はバージョン3.0にて、varキーワードをもちいたローカル変数へんすう宣言せんげんがた推論すいろん導入どうにゅうした。制約せいやくのひとつとして、宣言せんげん初期しょきともな必要ひつようがある。forぶんforeachぶん、usingぶんのスコープ変数へんすうにも利用りようできる。宣言せんげん初期しょきぶん右辺うへんがメソッドグループや匿名とくめい関数かんすうラムダしきおよび匿名とくめいメソッド)の場合ばあいには適用てきようできない[2]

// かた推論すいろんもちいないかた
string s1 = "文字もじれつ";
System.Console.WriteLine(s1.GetType()); // System.String
// かた推論すいろんもちいたかた
var s2 = "文字もじれつ";
System.Console.WriteLine(s2.GetType()); // System.String

var now = System.DateTime.Now;
System.Console.WriteLine(now.GetType()); // System.DateTime

now = 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による一部いちぶ変数へんすう宣言せんげんがた推論すいろん導入どうにゅうした[3]適用てきよう可能かのう範囲はんいは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によるもどかた推論すいろん導入どうにゅうした[4]後継こうけい規格きかく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;
}

総称そうしょうがたかた推論すいろん

編集へんしゅう

C++では、関数かんすうテンプレートたいしてテンプレートじつ引数ひきすう具体ぐたいてき型名かためい)を明示めいじてきあたえてかた決定けっていすることもできるが、曖昧あいまいさがない場合ばあいかぎり、関数かんすうしのじつ引数ひきすうおうじてかた推論すいろんさせることもできる。

#include <iostream>
#include <cmath>
template<typename T> T getVectorLength(T x, T y, T z) {
    return std::sqrt(x * x + y * y + z * z); // std::sqrt() には double あるいは float をるオーバーロードが存在そんざいする。
}
int main() {
    const double len1 = getVectorLength<double>(1, 2, 3); // double getVectorLength(double, double, double)
    const float len2 = getVectorLength(1.0f, 2.0f, 3.0f); // float getVectorLength(float, float, float)
    std::cout << len1 << std::endl;
    std::cout << len2 << std::endl;
}

テンプレートかり引数ひきすうT関数かんすうテンプレートにおけるかり引数ひきすうかた宣言せんげんが、参照さんしょうT&あるいはポインタT*であったり、ユニバーサル参照さんしょうT&&であったりする場合ばあいは、推論すいろん結果けっかとしてさだまるかたことなる場合ばあいもある。

C++17ではクラステンプレートのテンプレート引数ひきすう推論すいろんすることもできるようになった[5]

template<typename T> struct Vector3 {
    T x, y, z;
    Vector3(T ax, T ay, T az) : x(ax), y(ay), z(az) {}
};
int main() {
    Vector3<double> v1(1.0, 2.0, 3.0); // C++03 以前いぜんでも利用りよう可能かのうな、従来じゅうらいのコンストラクタしによる実体じったい
    Vector3<double> v2 { 1.0, 2.0, 3.0 }; // C++11 以降いこうの uniform initialization を使用しようした実体じったい
    Vector3 v3(1.0, 2.0, 3.0); // C++17 以降いこうでのみ有効ゆうこう。Vector3<double> に推論すいろんされる。
    Vector3 v4 { 1.0, 2.0, 3.0 }; // 同上どうじょう
}

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<>();

無名むめい関数かんすうかた推論すいろんれい

編集へんしゅう

無名むめい関数かんすうかた推論すいろんにおいては、複雑ふくざつ状況じょうきょう発生はっせいする。

C#れい以下いかしめす。

// 複数ふくすうのデリゲートがた定義ていぎ
delegate void TwoStringAction(string left, string right);
delegate void OneParamAction(object o);
delegate void TwoParamAction(object o, EventArgs e);
delegate void TwoIntegerAction(int x, int y);

// メソッドのオーバーロードを用意よういする。有効ゆうこうするオーバーロードの種類しゅるいにより、かた推論すいろん可否かひ変化へんかする。
static void SomeMethod(TwoStringAction action) { /* Pattern 1 */ }
//static void SomeMethod(OneParamAction action) { /* Pattern 2 */ }
//static void SomeMethod(TwoParamAction action) { /* Pattern 3 */ }
//static void SomeMethod(TwoIntegerAction action) { /* Pattern 4 */ }

static void Main() {
    // メソッドのオーバーロードがPattern 1のみの場合ばあいすべてのぶん有効ゆうこう(かた推論すいろん可能かのう)である。
    SomeMethod((o, e) => { /*No-op*/ }); /* 1ぎょう */
    SomeMethod((o, e) => { o = o + e; }); /* 2ぎょう */
    SomeMethod((o, e) => { o = "" + o + e; }); /* 3ぎょう */
    SomeMethod(delegate { /*No-op*/ }); /* 4ぎょう */
}
  • SomeMethod(OneParamAction action)くだり有効ゆうこうにすると、Mainメソッドの4ぎょうかた推論すいろんかなくなる。
    • 4ぎょう匿名とくめい関数かんすう引数ひきすうリストを省略しょうりゃくできるため、1引数ひきすう、2引数ひきすうのどちらのデリゲートがた曖昧あいまいになる。
  • SomeMethod(TwoParamAction action)くだり有効ゆうこうにすると、Mainメソッドの2ぎょう以外いがいかた推論すいろんかなくなる。
    • 1ぎょうのラムダは、2引数ひきすうかえさない複数ふくすうのデリゲートがたがある場合ばあい曖昧あいまいとなる。
    • 3ぎょうのラムダは、代入だいにゅうしき左辺さへんにあるostring格納かくのうできるかたでなければならないが、
      一方いっぽう右辺うへんoe暗黙あんもくToString()されるため、いずれのかたであってもよく、TwoStringAction TwoParamActionあいだ曖昧あいまいとなる。
    • 2ぎょうのラムダは、oeあいだ適切てきせつoperator+定義ていぎされなければならず、TwoStringActionかた推論すいろんされる。
  • SomeMethod(TwoIntegerAction action)くだり有効ゆうこうにすると、Mainメソッドの3ぎょう以外いがいかた推論すいろんかなくなる。
    • 2ぎょうのラムダは、oestringintのいずれでもoperator+実行じっこうできるため、TwoStringActionTwoIntegerActionあいだ曖昧あいまいとなる。
    • 3ぎょうのラムダは、代入だいにゅうしき左辺さへんにあるostring格納かくのうできるかたでなければならないため、TwoStringActionかた推論すいろんされる。

デリゲートがた引数ひきすうるメソッドを複数ふくすう用意よういする場合ばあい、オーバーロードではなく別名べつめいのメソッドとすることで、この複雑ふくざつせい回避かいひできる。

動的どうてきがた言語げんごにおけるかた推論すいろん

編集へんしゅう

動的どうてき型付かたつおこな言語げんご場合ばあい文法ぶんぽうてきには型付かたつけがおこなわれず、あらゆるかた可能かのうせい考慮こうりょして処理しょりすすめる必要ひつようがあるため、処理しょりおそくなる原因げんいんとなる[6]JITコンパイラによって高速こうそくはか場合ばあいかた推論すいろんによりあるデータを「特定とくていかた」としてあつかうことが可能かのうであれば、そのかたわせた処理しょりだけをすることで高速こうそくおこなえる[6]

JavaScript では、Webブラウザ分野ぶんやでは高速こうそくとくもとめられている[7]ため、2011ねん12月20にちにリリースされたFirefox 9から高速こうそくのためかた推論すいろん技術ぎじゅつ採用さいようしている[8]

Groovy 2.0 ではコンパイルがた検査けんさ @TypeChecked機能きのうをつけたが、かた指定していしていない変数へんすうたいしても、かた推論すいろん利用りようして変数へんすうかたり、かた検査けんさおこなっている。同様どうように Groovy に対応たいおうした IntelliJ IDEA などの統合とうごう開発かいはつ環境かんきょうかた推論すいろん利用りようしてアシストをおこなっている。

脚注きゃくちゅう

編集へんしゅう
  1. ^ C言語げんご関数かんすうもどかた省略しょうりゃくした場合ばあいintかえすと仮定かていする仕様しようになっているが、これをかた推論すいろんとはばない。
  2. ^ 暗黙あんもくてきかた指定していされるローカル変数へんすう - C# プログラミング ガイド | Microsoft Docs
  3. ^ auto - cpprefjp C++日本語にほんごリファレンス
  4. ^ decltype - cpprefjp C++日本語にほんごリファレンス
  5. ^ クラステンプレートのテンプレート引数ひきすう推論すいろん - cpprefjp C++日本語にほんごリファレンス
  6. ^ a b Type Inference brings JS improvements to Firefox Beta Brian Hackett, 2011ねん11がつ10日とおか(2011ねん12月24にち閲覧えつらん)。
    かた推論すいろんにより Firefox Beta の JavaScript が高速こうそくしましたうえ記事きじ和訳わやく)、2011ねん12月24にち閲覧えつらん
  7. ^ 一色いっしき政彦まさひこ (2011ねん4がつ22にち). “Internet Explorer 9正式せいしきばんレビュー”. @IT. December 24, 2011閲覧えつらん
  8. ^ Mozilla、「Firefox 9」の正式せいしきばんをリリース 「かた推論すいろん技術ぎじゅつで45%高速こうそく”. ITmedia (2011ねん12月21にち). December 24, 2011閲覧えつらん