Node.jsでOption型を学ぶ

Option 型というものがある。

概要部分を Google 翻訳に突っ込むと、

プログラミング言語関数型プログラミング言語ほど)と型理論では、オプション型または多分型は、任意の値のカプセル化を表す多型です。 例えば、それが適用されるときに意味のある値を返すこともあれば返さないこともある戻り値の型の関数として使用される。 これは、空(名前がNoneまたはNothing)または元のデータ型A(Just AまたはSome Aと書かれた)をカプセル化するコンストラクタで構成されます。関数型プログラミングの外では、これらはヌル可能型と呼ばれます。

要するに nullable なものをうまく扱うための仕組みっぽい。たとえばある関数から返ってきた結果を受けとって変数とするとき、その変数が値をちゃんと持っているかもしれないし、nullかもしれない。そういう状況はたくさんある。 nullable な値を扱うときには null チェックの if 文を書かなければいけないが、だいたいそういうのは忘れてしまう。しかも null チェックを忘れるのはバグのもとだ。なので、 null チェックを明示的にやっていこうぜ、と。

Rust の例を見ると、Option 型はパターンマッチと組み合わせると気持ち良いみたいだ。

(以下 Rust による Option 型の例。 Wikipedia より。)

fn main() {
    // This function uses pattern matching to deconstruct optionals
    fn compute(x: Option<i32>) -> String {
        match x {
            Some(a) => format!("The value is: {}", a),
            None    => format!("No value")
        }
    }

    println!("{}", compute(Some(42))); // The value is: 42
    println!("{}", compute(None)); // No value
}

Node.js にはパターンマッチはないので、 Option 型を輸入しても十分な美味しさは味わえないかもしれない。

だが、 Option 型(またはそれっぽい仕組み)を使うことには一般的に次のメリットがある。

  • 明示的な null チェック
  • null チェックを一箇所に集められる

たとえば次のようなコードを考えてみよう。

function findSomeJson (name) {
  let user = User.findByName(name)
  if (!user) {
    return '{ found: false }'
  }
  let info = SomeInfo.findByUser(user)
  if (!info) {
    return '{ found: false }'
  }
  let data = SomeData.findByInfo(info)
  if (!data) {
    return '{ found: false }'
  }
  return JSON.stringify(data)
}

アホくさいコードだが、ここで言いたいのは、何か値を取って来ようとするたびに、値が空であるかもしれない。それで値が空であるかどうかの判定を逐一入れている。

そこで Option 型を導入してみよう。 npm パッケージにちょうど option というのがある。

Node.js で Option 型が使える。これで上記のコードを直してみる。

const option = require('option')

function findSomeJson (name) {
  return option.fromNullable(name)
               .map(SomeInfo.findByUser)
               .map(SomeData.findByInfo)
               .map(JSON.stringify)
               .valueOrElse('{}')
}

すっきりした。やっていることは、こう。

  1. option.fromNullable でオプション型にラップする
  2. .map(func) は値が存在するときにだけ func を適用する
  3. 最後の valueOrElse で値が存在しないときのデフォルト値を与える

if 文が減った(条件分岐は最後の valueOrElse だけ)。これが null チェックを一箇所に集めるということだ。

実際に Option 型をわざわざ使うかはわからないが、考え方としておもしろいと思った。