0
0

More than 3 years have passed since last update.

SATySFiのoption型を扱おう

Posted at

これは「SATySFi Advent Calendar 2019」の17日目の記事です。

16日目はbd_gfngfnさんでした。18日目はmonaqaさんです。

はじめに

この記事ではSATySFiのoption型について解説します。

対象はSATySFiの初心者ですが、The SATySFibookを読み終わっていることを仮定します。

option引数の使いどころ

option型は答えが無いときがあるときによく使われます。

答えが無いときとは、例えば「リストの先頭を取り出す関数に空リストを与えた時」など、回復不能で致命的なエラーではないが適切な値を設定できないときのことです。

こんなものを使えてどう嬉しいかと疑問になるかもしれませんが、適切な値がわからなかったときに安易にエラーを返すと、単純になる代わりにすぐにエラーが出てなかなかコンパイルできないパッケージ・文書ができてしまいます。

また、関数の型から失敗する可能性のある操作を行う関数であるということがすぐにわかることも魅力の一つです。
失敗する可能性があることがわかればそれについて事前に初期値を用意しておくなどの対応が可能になります。

option型を返す関数は多くの場所で定義されており、satysfi-baseのlist-extパッケージではなんと14個も定義されています。

使えると非常に便利で汎用性も高まるのでぜひマスターしましょう。

option型の中身

option型はint optionstring optionなどのように型名 optionという形をしており、どんな型(ユーザーが独自に定義した型であっても)にでも対応できます。

さて、この万能感あふれるoption型はこのような形をしています(実際は内部で定義されているのですが、もしパッケージの形で実装するならこうなるでしょう)。

type 'a option = None | Some('a)

何もないことを表すNoneとそれ以外の場合の値を保持するSome(v)という二通りだけです。単純ですね。

例えば、さきほど例で出した「リストの先頭を取り出す関数(head)」に値を与えてみて結果を見てみましょう。

head [1;2;3;4]

=> Some(1)

head [`1`;`2`;`3`]

=> Some(`1`)

head []

=> None

なんとなくわかってきたと思います。

option型の中身の取り出し方

さて、このままではSome(v)という形で囲われてしまっていてそのままでは単純な足し算すらできません。なのでこの中身のvを取り出すことにしましょう。

しかし、単純に取り出そうとしてもその値がNoneである時のことも考慮しないといけません。そこで、ここで「パターンマッチ」という構文を使います。

詳しくはThe SATySFibookの46ページを読んで欲しいのですが、

match expr with
  | pattern_1 -> expr_1
  | pattern_2 -> expr_2
  .
  .
  .
  | pattern_n -> expr_n

という風にして値などの中身に応じて処理を変えることができる便利機能です。
例えば、「1のときには`1`を、2のときには`2`を、それ以外では`n`を返す関数」は

let f n =
  match n with
  | 1 -> `1`
  | 2 -> `2`
  | _ -> `n`

と書けます。ここで最期に使っている_はワイルドカードと呼ばれる「それ以外全てのとき」を表せるパターンです。

if文でも書けるような内容ですが、複数の場合を書き分けるときに重宝します。

さて、これを使ってoptionの中身を取り出しますが、これは単純に、

match opt with
| Some(v) -> v_1
| None -> v_2

というようにしてSome(v)のときとNoneのときで場合分けをするだけです。

さて、「head関数でint listの先頭を取り出すが、もし空リスト出会ったら0を返す」という関数fを定義しようとするとどうなるでしょう。

答えはこうなります。

let f lst =
  match head lst with
  | Some(n) -> n
  | None -> 0

option型を扱うための関数

標準ライブラリに含まれているopiton.satygファイルで以下の3つの関数が提供されています(モジュール名はOption)。

  • map : ('a -> 'b) -> 'a option -> 'b option
  • from : 'a -> 'a option -> 'a
  • bind : 'a option -> ('a -> 'b option) -> 'b option

  • map関数は中身がNoneのときはそのままNoneを返し、Some(v)だったときはvに対して最初に受け取っていた関数を適用します。

  • fromは中身がNoneだったときには最初に受け取っていた初期値を返し、Some(v)だったときにはそのvを返す関数です。

  • bindmap関数とほとんど同じですが、受け取る関数自体もoption型を返します。

標準ライブラリ以外にもsatysfi-baseのoption-extパッケージでも多くの関数が用意されています。

おわりに

option型の扱い方がわかったと思います。

上手く使いこなすと非常に便利な機能ですのでぜひ活用していってください。

本当はオプション引数についても触れられればよかったのですが、またの機会ということで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0