目次

序文
第1章 はじめに
1.1 プログラムと型
1.2 OCamlのインストール
第2章 基本的なデータと型
2.1 整数
2.2 実数と浮動小数点数
2.3 文字
2.4 文字列
2.5 真偽値
2.6 ユニット値
2.7 その他のデータと型
第3章 変数と関数
3.1 変数
3.2 スコープと局所変数
3.3 関数
第4章 条件分岐
4.1 if式
第5章 データと型とパターンマッチ
5.1 タプル
5.2 非カリー化関数
5.3 パターンマッチ
5.4 リスト
第6章 再帰
6.1 再帰関数
第7章 関数の引数
7.1 ラベル付き引数
7.2 オプション型とオプショナル引数
第8章 その他のデータと型
8.1 レコード
8.2 独自の型定義
8.3 バリアント
第9章 木構造とアルゴリズム
9.1 木構造と操作関数
9.2 2分探索木
第10章 エラーハンドリング
10.1 戻り値によるエラーハンドリング
10.2 オプション型によるエラーハンドリング
10.3 Result型によるエラーハンドリング
10.4 例外によるエラーハンドリング
第11章 モジュールとファイル分割
11.1 モジュール
11.2 シグネチャ
11.3 ファンクタ
11.4 Duneの利用
11.5 ファイル分割とモジュール
11.6 外部モジュールの利用
参考文献

第1章 はじめに

 プログラミングはプログラムを作成する行為です。本章ではプログラミングの基本を学習する前に、プログラムとは具体的に何を指すのか、そしてプログラミングを行う上で利用する型や型システムについて整理します。

 加えて、学習を進めるために有用なOCamlのツール群のインストール方法についても説明します。

1.1 プログラムと型

 プログラムまたはコンピュータプログラム(以下、プログラム)の定義は、文脈や時代によって様々な表現があります。例えば「Principles of Information」という書籍では、「コンピュータを操作するためのもの」と説明されています。人がプログラムを書くためにはプログラミング言語が利用されます。プログラミング言語を使用して書かれたプログラムは一般的にソースコードと呼ばれます。ソースコードには、コンピュータを操作したり値を生成したりするための式や文が記述されています。言い換えれば、プログラムは式と文を組み合わせて何らかの値 (value)を算出する仕組みと考えることができます。

 式から値を得るための計算過程を評価 (evaluation)と言います。いくつかのプログラミング言語およびOCamlには型 (type)という概念があり、これは値がどのような種類のデータに分類されるかを示すものです。

 型は意味のない計算を防ぐ仕組みであり、例えば「1 + "ほげ"」のように数値と文字列を可算するといった無意味な計算を未然に防ぎます。このようにデータの種類を確認することを型チェック (type check)と言います。プログラミングにおいて無意味な計算によって発生するエラーを型エラー (type error)と言います1

 また、型チェックによってエラーを防ぐ目的を持つ仕組みを型システム (type system)と言います。なお、型、型チェック、型システム等の定義はプログラミング言語によって異なることに留意してください。例えば「型システム」について、書籍「型システム入門」では十分に意味のある定義を与えることが難しいことを指摘しつつ、次のような定義を示しています。

型システムとは、プログラムの各部分を、それが計算する値の種類に沿って分類することにより、プログラムがある種の振る舞いを起こさないことを保証する、計算量的に扱いやすい構文的手法である。

 OCamlは型システムを持つためプログラム実行の前にこのような無意味な計算を発見でき、一度チェックが完了すると型の不一致が発生しない性質が保証されています。このような性質を型安全性 (type safety)と言います2

 このような理由から、OCamlのように型や型システムを持つプログラミング言語を学習することは有用です。

1.2 OCamlのインストール

 本書ではOCamlのコード例を用いて説明します。OCamlのコードを手元で確かめるにはocamlコマンドまたはutopコマンドが必要です。本章ではUbuntu OSにおけるインストール方法を紹介します。

 Ubuntuにログインしターミナル(端末)を起動します。始めに、下記コマンドを実行しパッケージをアップデートします。

リスト1.1: apt update

% sudo apt update

 必要に応じてパッケージのアップグレードも行います。

リスト1.2: apt upgrade

% sudo apt upgrade -y

 次にopamというOCamlのパッケージマネージャをインストールします。これをインストールするとocamlコマンドもインストールされます。

リスト1.3: apt install opam

% sudo apt install opam -y

 opamをインストール後、下記コマンドを実行しopamの初期化を行います。

リスト1.4: opam init

% opam init --bare -a -y

 OCamlにはutopというREPL(Read-Eval-Print Loop)というツールが存在します。REPLとは実行環境の1つであり、プログラマが入力した文字列を受け取ってそれを解釈および実行し結果を返すプログラムです。このようなプログラムは対話型トップレベル (Interactive toplevel)とも呼ばれます。utopは結果を返した後で再び入力を待ち受けます。utopは補完機能等を備えているため、本書ではutopの利用を推奨します。utopをインストールするには、以下のコマンドを実行します。

リスト1.5: opam install utop

% opam install utop

 コンソールにutopと入力し、utopを起動します。

リスト1.6: utop

% utop

 終了するにはCtrl + dを入力します。

1. OCamlでは型エラーに準ずるエラーはありますが、型エラーという用語は定義されていません。

2. 他のプログラミング言語では、プログラムを実行して初めて型の不整合に気づくことがあります。型エラーが発生するとプログラムがクラッシュしてしまう場合もあります。



第2章 基本的なデータと型

 この章では、プログラミングの基本となるデータとその型を説明します。本章を読み進める際には、初学者の方はお手元にインタプリタ(例:utopコマンドやocamlコマンド等)を起動して、確かめながら読み進めてみてください。

2.1 整数

 整数とは0とそれに1ずつ加えて得られる数(自然数、例えば1、2、3...)および1ずつ引いて得られる数(例えば、-1、-2、-3...)の総称です。OCamlにおいて整数はintという型で表現されます。それではutopで整数をインタプリタに与えた例をみてみます。

リスト2.1: utop

utop # 2 ;;
- : int = 2

 例えば1行目で2 ;;と入力した時の結果が2行目に表示されており、2行目は「直前(-)に入力した結果の型はintであり、結果の値は2である」ことを表しています1

 int型には算術演算として(紙面上で計算する時に用いる時と同じように)次の四則演算があります。可算を表す+、減算を表す-、乗算を表す*、除算の商を表す/、余りを表すmodがあります。

リスト2.2: utop

utop # 4 + 3 ;;
- : int = 7
utop # 5 - 3 ;;
- : int = 2
utop # 6 * 3 ;;
- : int = 18
utop # 7 / 3 ;;
- : int = 2
utop # 8 mod 3 ;;
- : int = 2

 このように演算に用いる記号を演算子 (Operator)と言います。特に四則演算に用いる演算子は四則演算子とも呼ばれます。

2.2 実数と浮動小数点数

 実数とは連続した量を表すための数であり、イメージとしては数直線上に並べることができる全ての数を言います。実数は円周率(3.14...)等の小数点表現や指数表現はもちろん、前節で説明した整数も含まれます。このように実数には、整数のようにa/b(a、bともに0でない)の形で表現できる有理数と、円周率(3.14...)のようにa/b(a、bともに0でない)で表現できない無理数があります。

 コンピュータでは実数を正確に表現できないため、実数の近似値を表現するための浮動小数点数 (floating-point number)という方式を用います。OCamlでは浮動小数点数をfloatという型で表現します。utopで浮動小数点数をインタプリタに与えた例をみてみます。

リスト2.3: utop

utop # 3.14 ;;
- : float = 3.14

 float型にもint型同様に四則演算が定義されています。注意点として、OCamlはint型とfloat型を明確に区別しています。そのため、int型のための四則演算である+等をfloat型に対して利用できず、float型用の四則演算を利用する必要があります。float型の可算を表す+.、減算を表す-.、乗算を表す*.、除算の商を表す/.があります2

 float型の演算の例を以下に示します。

リスト2.4: utop

utop # 3.14 +. 4.0 *. 5.0 /. 3.0 ;;
- : float = 9.80666666666666664

 また、小数点以下が0の場合は、その0を省略することができます。次の例は前述の式と同じ式になります。

リスト2.5: utop

utop # 3.14 +. 4. *. 5. /. 3. ;;
- : float = 9.80666666666666664

 加えて、float型にはべき乗を求める関数**も定義されています。なお**にはドット(.)が付かないことに注意してください。

リスト2.6: utop

utop # 3. ** 3. ;;
- : float = 27.

 ここで、float型の四則演算をint型に適用した場合またはその逆(int型の四則演算をfloat型に適用した場合)について説明します。float型の四則演算ではfloat型の値のみ受け取ることができ、int型の四則演算はint型の値のみを受け取ることができます。四則演算に渡される型が1つでも合わない場合はエラーとなります。

リスト2.7: utop

utop # 3 ** 3. ;;
Line 1, characters 0-1:
Error: This expression has type int
       but an expression was expected of type float
Hint: Did you mean `3.'?

 上記はint型の3にfloat型の演算子である**を適用してしまった例です。インタプリタが出力した1行目にはエラーとなった箇所が示されており、「与えられた入力の1行目の0から1文字目でエラーが発生しました」という内容になります。また、2行目にはエラーメッセージが示され、内容は「この式はint型を受け取ったが、式はfloat型を期待していました」となります。さらに3行目にはエラー箇所に関するヒントとして「3.ではないですか?」という内容が示されています。

2.3 文字

 文字は英数字1文字を表すもので、OCamlは8bits(1byte)で表現される1文字を表します3。文字を扱いたい場合は引用符'...'で囲みます。OCamlにおいて文字はcharという型で表現されます。utopで文字をインタプリタに与えた例をみてみます。

リスト2.8: utop

utop # 'a' ;;
- : char = 'a'

 なお、文字は1文字の表現であるため'ab'のように2文字をシングルクォート(')で囲むことはできません。加えて、空文字('')を書くこともできません。

リスト2.9: utop

utop # 'ab' ;;
Error: Syntax error
utop # '' ;;
Error: Syntax error

2.4 文字列

 OCamlにおいて文字列は単なる文字のリスト(文字を連結したもの)ではありません。そのため文字とは異なるデータであり、文字列を扱いたい場合は引用符"..."で囲みます。OCamlにおいて、文字列はstringという型で表現されます。utopで文字列をインタプリタに与えた例は以下のとおりです。

リスト2.10: utop

utop # "abc" ;;
- : string = "abc"

 文字とは異なり、文字列は1文字であってもダブルクォート(")で囲まれていれば、正しく文字列として評価されます。また、空の文字列("")も書くことができます。

リスト2.11: utop

utop # "a" ;;
- : string = "a"
utop # "" ;;
- : string = ""

 さらに、文字列同士を結合する演算子である^が定義されています。

リスト2.12: utop

utop # "a" ^ "bc" ^ "def" ;;
- : string = "abcdef"

2.5 真偽値

 真偽値は真または偽を表す値です。真はtrueで表し、偽はfalseで表します。OCamlにおいて、真偽値はboolという型で表現されます。真偽値をインタプリタに与えた例をみてみます。

リスト2.13: utop

utop # true ;;
- : bool = true
utop # false ;;
- : bool = false

 整数や浮動小数点数では四則演算が定義されていましたが、真偽値には論理演算が定義されています。OCamlには、かつを表す&&、またはを表す||、否定を表すnotがあります。

リスト2.14: utop

utop # true && true ;;
- : bool = true
utop # true && false ;;
- : bool = false
utop # false && false ;;
- : bool = false

utop # true || true ;;
- : bool = true
utop # true || false ;;
- : bool = true
utop # false || false ;;
- : bool = false

utop # not true ;;
- : bool = false
utop # not false ;;
- : bool = true

 また四則演算では加減算よりも乗除算の方が優先度が高い(つまり乗除算を先に計算する)というルールがありますが、真偽値においては優先度の高い順から「否定>かつ>または」というルールがあります。そのため、次の2つの式は同じ意味となります。

リスト2.15: utop

utop # not (true && false || not true && not false) ;;
- : bool = true
utop # not ((true && false) || ((not true) && (not false))) ;;
- : bool = true

 次に、2つまたは2つ以上の値を比較した結果としてbool型を返す方法について説明します。それは比較演算子を用いる方法です。OCamlには比較演算子として、2つの値が等しいかどうかを表す=、2つの値が異なるかどうかを表す<>、与えられた1つ目の値が2つ目の値よりも大きいかどうかを表す>、与えられた1つ目の値が2つ目の値以上かどうかを表す>=、与えられた1つ目の値が2つ目の値よりも小さいかどうかを表す<、与えられた1つ目の値が2つ目の値以下かどうかを表す<=等があり、これらを用いてその大小を比較することができます4

リスト2.16: utop

utop # 3 = 3 ;;
- : bool = true
utop # 3 <> 3 ;;
- : bool = false
utop # 3 > 2 ;;
- : bool = true
utop # 3 >= 2 ;;
- : bool = true
utop # 3 < 2 ;;
- : bool = false
utop # 3 <= 2 ;;
- : bool = false

utop # "abc" = "abc" ;;
- : bool = true
utop # "abc" <> "abc" ;;
- : bool = false

 上記の比較演算子を文字や文字列に適用することも可能です。文字や文字列においては、ラテン文字(abc...xyz、ABC...XYZのこと)の先頭から末尾に行くに連れて大きな値となっており、ちょうど辞書の並びと一致するようになっています。そのため、文字や文字列も整数や実装と同じようにその大小を比較できます。

リスト2.17: utop

utop # 'b' > 'a' ;;
- : bool = true
utop # 'b' < 'a' ;;
- : bool = false

utop # "def" > "abc" ;;
- : bool = true
utop # "def" < "abc" ;;
- : bool = false

 なお、文字や文字列に大小関係があるからといって、整数や浮動小数点数等の異なる型と比較することはできません。

リスト2.18: utop

utop # 'a' < 1 ;;
Line 1, characters 6-7:
Error: This expression has type int
       but an expression was expected of type char

utop # "pi" = 3.14 ;;
Line 1, characters 7-11:
Error: This expression has type float
       but an expression was expected of type string

 最後に蛇足な情報ですが、trueとfalseにも値の大小があるため同様の比較が可能です。

リスト2.19: utop

utop # true > false ;;
- : bool = true
utop # true < false ;;
- : bool = false

 しかし、これはプログラムの読み手にとって理解しづらい可能性があるため、できるだけ避ける方が良いでしょう。

2.6 ユニット値

 ユニット値 (Unit value)()という特別な値です。ユニット値はunitという型で表現され、()という値のみ存在します。ユニット値は戻り値を返す必要がない場合に用います。例えば標準出力への印字といった、いわゆるI/O実行があげられます。

 OCamlには標準出力に表示するための関数があり、その1つがprint_endlineです。

リスト2.20: utop

utop # print_endline ;;
- : string -> unit = <fun>

 print_endlineを用いて標準出力に文字列を表示する例を以下に示します。

リスト2.21: utop

utop # print_endline "abcdefg" ;;
abcdefg
- : unit = ()

 インタプリタの出力は「直前(-)に入力した結果の型がunit型で、その値が()である」ことを意味します。

ユニット値と副作用

 プログラミングをやっていると副作用 (side effect)という言葉を聞いたことがあるかもしれません。副作用とは「式が主たる計算(作用)以外の、何らかの状態を変化させる作用」と表現されます。主たる計算以外とは、結果を返すために実行された計算以外の観察可能な計算を指します。具体的には変数(後述)の上書き、ファイル操作やネットワーク通信などのI/O実行を伴う操作が該当します。

 OCamlを含む副作用を表現可能なプログラミング言語では、式が副作用を持つかどうかに関心があり、式が最終的にどのような値を返すかに興味がない場合があります。その場合、その計算全体の戻り値をユニット値()とすることで副作用を持つことを明示できます。

 ユニット型はCやJavaのvoid型に相当します。voidという用語から空の型を連想しがちですが、実際はUnit型と近い役割を持つとも考えられます。

2.7 その他のデータと型

 OCamlにおける基本的なデータは前述のとおりですが、この他にもタプル、リスト、レコード等のデータと型が存在します。これらのデータと型については後述します。

1. 末尾の2つのセミコロン(;;)は、入力の終わりをインタプリタに教えています。

2. int型の四則演算とは違い、float型の四則演算はドット(.)がついた形になっています。

3. プログラミング言語によってはOCaml同様にchar型を8bits(1byte)で表現するものもあれば、それでないものもあります。

4. 他のプログラミング言語(例えばC言語)では比較演算子として、2つの値が等しいかどうかを表す==、2つの値が異なるかどうかを表す!=を用いる点が異なります。

試し読みはここまでです。
この続きは、製品版でお楽しみください。