型推論とは? わかりやすく解説

Weblio 辞書 > 同じ種類の言葉 > 人文 > 論理 > 推論 > 型推論の意味・解説 

型推論

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2024/02/21 13:38 UTC 版)

型推論(かたすいろん、: 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. 2011年12月24日閲覧。
  8. ^ Mozilla、「Firefox 9」の正式版をリリース 「型推論」技術で45%高速に”. ITmedia (2011年12月21日). 2011年12月24日閲覧。

型推論

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2022/06/05 19:25 UTC 版)

Rust (プログラミング言語)」の記事における「型推論」の解説

Rustコンパイラ変数への代入時(variable = value)、変数の型を値の型に基づき型推論する。変数の宣言には必ずしも型を決定するための初期値を必要としない変数の宣言時に初期値与えられ場合は「変数の型」は「初期値の型」であると型推論がなされるが、初期値与えられなかった場合以降ブロックコード中のその変数へ値が初め代入され時に左辺変数の型」は「右辺代入する値の型」であると型推論がなされる変数への代入が型不一致により失敗した場合にはコンパイル時にエラー検出する

※この「型推論」の解説は、「Rust (プログラミング言語)」の解説の一部です。
「型推論」を含む「Rust (プログラミング言語)」の記事については、「Rust (プログラミング言語)」の概要を参照ください。


型推論

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2021/08/17 21:54 UTC 版)

C SharpとJavaの比較」の記事における「型推論」の解説

C# 3.0コンテキストキーワードvarによる限定された型推論が導入された。ローカル変数宣言時に、型を右辺から推論できるメソッド引数フィールドには使えないまた、ラムダ式戻り値および仮引数は型推論により決定されるラムダ式仮引数は型を省略することで型推論されるが、型推論が困難な場合には明示的に型を指定するJava 7で導入されダイヤモンド演算子<>は宣言文右辺ジェネリクスの型を省略できる程度のものでしかなかったが、Java 10ではC#同様に予約型名varによるローカル変数の型推論が導入された。Java 8で導入されラムダ式では、仮引数の型を省略することで型推論されるが、さらにJava 11ではラムダ式仮引数の型にvar使用して型推論できるようになった

※この「型推論」の解説は、「C SharpとJavaの比較」の解説の一部です。
「型推論」を含む「C SharpとJavaの比較」の記事については、「C SharpとJavaの比較」の概要を参照ください。


型推論

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2020/11/20 02:06 UTC 版)

Haskell」の記事における「型推論」の解説

Haskell では関数データ型明示しなくても処理系自動的に型を推論する。以下は型の宣言省略し本体のみを宣言した引数平方返す関数 square である。 square x = x * x この場合 square の型は型推論され、次のように明示的に型を宣言したのと同じになるsquare :: (Num a) => a -> asquare x = x * x この宣言は、「Numのインスタンスである a の型の値を引数にとり、a の型の値を返す」と読める。ここでは「*」演算子適用可能な最も広い型である Num a選択されており、整数浮動小数点数有理数のような Num のインスタンスであるあらゆる型の値を渡すことができる。外部公開するような関数定義するときは、型推論によって自動的に選択される最も広い型では適用可能な範囲が広すぎる場合もある。Integer のみを渡せるように制限する場合は、次のように明示的に型を宣言すればよい。 square :: Integer -> Integersquare x = x * x 型推論のため、Haskell は型安全でありながらほとんどの部分で型宣言省略できる。なお、次のコードは型宣言必要な例である。read は文字列をその文字列があらわすデータ型変換する抽象化された関数である。 main = print (read "42") -- コンパイルエラー! このコードコンパイルエラーになる。read複数インスタンス実装されており、数値なら数値型変換する readリストならリスト変換する read というように型ごとに実装存在するHaskell の型は総て静的決定されなければならない。このコード場合プログラマread :: String -> Int という型をもつ実装read選択される期待しているであろうが、これはコンパイラによる静的型検査では決定できない。つまり、Haskell コンパイラread返り値受け取っている関数 print の型を検査し多数実装の中から適切な read選択しようとするが、printShowインスタンス存在するあらゆる型を引数にとるため、型推論によっても read の型を一意決定できない。これを解消するひとつの方法は、:: によって型を明示することである。 main = print ((read "42") :: Int) -- コンパイル成功read返り値Int明示している また、そもそも read返り値整数型しか取らない関数与えていればあいまいさ生じず、型推論は成功する。 printIntOnly :: Int -> IO ()printIntOnly x = print xmain = printIntOnly (read "42") -- コンパイル成功他の言語、たとえば Javaこのような抽象的な関数書こうとしても、Java では返り値の値の型によって関数選択するようなことはできない引数の型によって選択するメソッドオーバーロード存在する)。そのため、関数実装ごとに別の名前をつけてプログラマ明示的に選択させて解決させることになる。この方法は簡潔わかりやすいが、抽象性の高さに基づく再利用性という点では Haskell のような多相には劣ってしまう。

※この「型推論」の解説は、「Haskell」の解説の一部です。
「型推論」を含む「Haskell」の記事については、「Haskell」の概要を参照ください。


型推論

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2022/08/09 03:08 UTC 版)

ユニフィケーション」の記事における「型推論」の解説

ユニフィケーションは型推論でも使われており、例え関数型言語 Haskell使われている。型推論を行う言語では型に関する情報をいちいち記述する必要がなく、ユニフィケーションデータ型誤り検出使われるHaskellの式 1:['a','b','c'] は型付け正しくない。なぜならリスト構築関数 : の型は a->[a]->[a] だが、第一引数 1 からポリモルフィックな型変数 a はInt型となるのに対して、['a','b','c'] の型は[Char]であり、a は同時に CharInt になることはできないからである。 型推論のアルゴリズム次のうになる: 任意の変数任意の表現単一化し、その表現インスタンス化する。理論によっては出現検査でこの規則制約課すこともある。 2つの型定数両者が同じ型のときのみ単一化される。 2つの型構築は、両者使用する型構築子が同じで、それらのコンポーネント型が再帰的単一化されるときのみ単一化される。 宣言的特徴から、ユニフィケーションが行われる順序通常重要ではない。

※この「型推論」の解説は、「ユニフィケーション」の解説の一部です。
「型推論」を含む「ユニフィケーション」の記事については、「ユニフィケーション」の概要を参照ください。


型推論

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2022/08/06 04:25 UTC 版)

型システム」の記事における「型推論」の解説

詳細は「型推論」を参照 静的型システム言語では型宣言を必要とし、基本的にプログラマすべての変数特定の型を明示的に関連付けなければならない。しかし、変数初期化時の右辺値や変数使われ方など、プログラマが型を明示せずともコード文脈から型を自動的に決定する型推論(type inference)の機能を持つ言語もある。例として、Haskellにおいて変数 x と y を加算する関数 f を定義してみる。 f x y = x + y ここで、x と y の型を特に明示していないが、+による加算数値のみに定義されているので、コンパイラは x と y は共に数値型であると推論できる。ゆえにプログラム中で f の引数として数値でない型(文字列リストなど)の値を渡して呼び出すとエラー報告するmain = do print (f 1 2) -- 3 print (f 1.0 2.0) -- 3.0 --print (f "hoge" "fuga") -- コンパイルエラー。 型推論の目的は、単にコード記述効率向上したり、型のミスマッチ起因するエラー軽減したりすることだけではない。ラムダ式匿名型など、コンパイラ内部で型を自動生成し、プログラマ具体的な型の名前をコード上で書き下すことができない場合もあり、そういった機能対応しなければならないという目的もあって型推論を導入した言語もある(C++C#など)。

※この「型推論」の解説は、「型システム」の解説の一部です。
「型推論」を含む「型システム」の記事については、「型システム」の概要を参照ください。

ウィキペディア小見出し辞書の「型推論」の項目はプログラムで機械的に意味や本文を生成しているため、不適切な項目が含まれていることもあります。ご了承くださいませ。 お問い合わせ

「型推論」の例文・使い方・用例・文例

Weblio日本語例文用例辞書はプログラムで機械的に例文を生成しているため、不適切な項目が含まれていることもあります。ご了承くださいませ。



型推論と同じ種類の言葉


英和和英テキスト翻訳>> Weblio翻訳
英語⇒日本語日本語⇒英語
  

辞書ショートカット

すべての辞書の索引

「型推論」の関連用語

型推論のお隣キーワード
検索ランキング

   

英語⇒日本語
日本語⇒英語
   



型推論のページの著作権
Weblio 辞書 情報提供元は 参加元一覧 にて確認できます。

   
ウィキペディアウィキペディア
All text is available under the terms of the GNU Free Documentation License.
この記事は、ウィキペディアの型推論 (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。 Weblio辞書に掲載されているウィキペディアの記事も、全てGNU Free Documentation Licenseの元に提供されております。
ウィキペディアウィキペディア
Text is available under GNU Free Documentation License (GFDL).
Weblio辞書に掲載されている「ウィキペディア小見出し辞書」の記事は、WikipediaのRust (プログラミング言語) (改訂履歴)、C SharpとJavaの比較 (改訂履歴)、Haskell (改訂履歴)、ユニフィケーション (改訂履歴)、型システム (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。
Tanaka Corpusのコンテンツは、特に明示されている場合を除いて、次のライセンスに従います:
 Creative Commons Attribution (CC-BY) 2.0 France.
この対訳データはCreative Commons Attribution 3.0 Unportedでライセンスされています。
浜島書店 Catch a Wave
Copyright © 1995-2024 Hamajima Shoten, Publishers. All rights reserved.
株式会社ベネッセコーポレーション株式会社ベネッセコーポレーション
Copyright © Benesse Holdings, Inc. All rights reserved.
研究社研究社
Copyright (c) 1995-2024 Kenkyusha Co., Ltd. All rights reserved.
日本語WordNet日本語WordNet
日本語ワードネット1.1版 (C) 情報通信研究機構, 2009-2010 License All rights reserved.
WordNet 3.0 Copyright 2006 by Princeton University. All rights reserved. License
日外アソシエーツ株式会社日外アソシエーツ株式会社
Copyright (C) 1994- Nichigai Associates, Inc., All rights reserved.
「斎藤和英大辞典」斎藤秀三郎著、日外アソシエーツ辞書編集部編
EDRDGEDRDG
This page uses the JMdict dictionary files. These files are the property of the Electronic Dictionary Research and Development Group, and are used in conformance with the Group's licence.

©2024 GRAS Group, Inc.RSS