📝

Haskellの型推論とは?(静的・動的型付けと比較)

2022/04/25に公開

この記事では、Haskellが持つ型推論について学びます。

Haskellでは型推論といって、データ型をコンパイル時に判定・補足する機能を持ちます。 従来の静的型付け・動的型付けと比較しながら、その特徴を考えていきます。

データ型とは何か?

これから扱うデータの種類・大きさをコンピュータに伝える情報です。 コンピュータのメモリには上限があるので、扱うデータの大きさをあらかじめ指定する必要があります。

データ型の物理的な形はメモリスペースです。ビルディングのようなメモリスペースの中に、10などの数値・HelloWorldなどの文字列を格納することになります。

量・質によって異なる

データの量・質によってもデータ型は異なります。10桁と1万桁では必要なメモリスペースは違いますし、100という数値とHelloWorldという文字列でも異なります。扱うデータの量・種類・大きさによってデータ型は使い分ける必要があります。

(補足)データ型が必要なのは式=値

プログラムは主に、文と式から構成されます(カンタンにいえば命令とデータのことです)。ここで注意して頂きたいのが、データ型を指定するのは式(=値)に対してです。文(=命令)にデータ型は指定できません。

■文(命令)
コンピュータに対する命令のこと
if文、for文、while文..など

文とはコンピュータに対する命令のことです。「〇〇しろ!」と指示を出していると考えると良いでしょう。命令の機能を呼び出すだけなので、文にデータ型は必要ありません。

■式(値)
値を返す要素のこと(データ型を持つ)
変数、関数、真偽値、計算式..など

式とは値を返す要素のことです。式(値)は多くの場合、データ型を持ちます。

型付けとは?

値に対してデータ型を指定する方法のことです。 値に対してデータ型を指定する方法には、主に2種類の方法があります。静的型付けと動的型付けです。

歴史的な経緯

具体的な内容に入る前に、歴史的な経緯を確認しておきます。

静的型付けの時代

従来の言語では多くの場合、値に対してあらかじめデータ型を指定する必要がありました。必要なメモリスペースをきちんとコンピュータに確保させて、そこへ自分たちの扱いたいデータを入れる..といった感じです。コンピュータの性能があまり良くないので、人間の方でこれを指定する必要がありました。

ですが全ての値にデータ型を指定するのは面倒ですよね。int num = 10のようにわかりきったモノも一々書くのはどうかなとも思います。また大規模開発がスタンダーになるにつれ、データ型の指定は複雑さ・冗長さを招く原因にもなりました。

動的型付けの時代

静的型付けの面倒さを克服するため登場したのが「動的型付け」です。動的型付けではプログラムが自動的に値が持つデータ型を指定してくれます。num = 10と書けば『numのデータ型はintだな..』と自動的に推測してれるわけです。コンピュータの性能が上がって、技術的にそのようなことが可能になった側面もあるでしょう。これによって開発する人がintやcharなどを指定しなくても、プログラムが自動的に補足&複雑さを軽減することができました。

メリット・デメリット

型を推測してくれる動的型付けによって、シンプルで簡潔にコードが記述できるようになったのです。めでたしめでたし..ではありません。

静的・動的型付けにはメリット・デメリットがあります。むしろ一周回って静的型付けの方が良いんじゃないか?という意見もあるくらいです。

動的型付けは実行段階までエラーを発見することができません。そのため大規模開発でエラーが起こった際には、探すのが非常に困難になります。プログラムをリリースしてからエラーが発見されるのもしばしばです。そんなワケで今まで両者は安全性・柔軟性のトレードオフの関係にありました。

Haskellの型推論とは?

データ型をコンパイル時に判定・補足する機能のことです。 カンタンにいえば静的型付け・動的型付けのよいと良いとこ取りができます。なぜこのようなことができるかといえば、与えられたコードから自動的にプログラムが型を推測してくれるからです。

例えばこちらの変数:xのデータ型は何でしょうか。こちらは数値を1増加させるzouka関数です。

zouka x = x + 1

この場合、コンパイラは自動的にxは数値型だと判断してくれます。というのもxに対して「+1」という演算を行なっているからです。なのでzouka関数に対して文字列"ABC"などを適用するとエラーになります。

型推論の機能を使えば、データ型を指定しなくてもコードを書くことができます。変数のデータ型を全て記述するのは非常に面倒な作業であり、プログラムが長たらしく、複雑になる原因となっていました。型推論のおかげで短く、明瞭で、簡潔なコードを書くことが可能となります。それは読む側にとってもとても楽です。

動的型付けと変わらないのでは?

しかしこれだけ見ると動的型付けと型推論は同じ仕組みのように思えますよね。しかし動的型付けと型推論は間違いの発見をするタイミングが異なります。間違いの発見のタイミングが違うだけで、両者の性能には大きな差が出てきます。

ソースコード〜実行まで

そもそも私たちが書いたソースコードを実行するには、

1、ソースコード
2、コンパイル(機械語へ翻訳)
3、実行

という3つのステップを踏みます。これら3つの段階で間違いの発見のタイミングが異なります。

  • 型推論:コンパイル段階でエラーを発見
    →コンパイルできたならエラーはほぼない
  • 動的型付け:実行段階でエラーを発見
    →値が変わったりするとエラーが起こる

微妙な差ですが大きな違いを生みます。

動的型付けの場合

動的型付けでは実行時にしか間違いは発見できません。プログラムを実行しないと型がどんな形で、どんな大きさなのかを判別することができないため、実際にプログラムを動作させないとわかりません。そのためエラーの発見が困難となり、大量のテストプログラムが必要となります。

型推論の場合

一方、型推論ならコンパイルの段階で間違いを発見できます。おかしなデータ型の組み合わせになっていれば、事前に知らせてくれます。実行時の型エラーはないので、一度コンパイル済みであればコードはほぼ正しく動きます。これによって簡潔な記述とバグ・エラーのないプログラムの両方を実現しています。

型変数・型クラスでも

ちなみに、データ型に柔軟に扱うための仕組みは他にも用意されています。それが型変数・型クラスです。型変数を使えば不特定多数のデータ型でも受け取れるようになるため、汎用的な関数を作るのに役立ちます。さらに型クラスの仕組みを使えば、型変数で受け取れるデータ型の種類を指定することができます。これもコンパイル時にしっかりと間違いを発見できるので、エラー・バグを起こさずこのような仕組みを使うことができるのです。

Discussion