Scrapdiary

DesigningとEngineeringの架け橋

例外(Exception)って何だろう?

いまいち理解できてなかったeval

例外(exception)を捕捉しようてevalを使ってみたけど、イマイチevalがわからない。

先日書いたDate::Simpleを使ったコードで、あり得ない日付を与えるとdieするようにしておいた部分を改良。上記を参考に、dieでperl自体が終了するのでは無く、例外が発生したかどうかをスクリプト側で把握できればと思い、試行錯誤でコードを書いてみた。evalには

  1. eval 評価・式
  2. eval {}(ブロック)

の二つの使い方があり、今回は例外トラップのためなので後者の方になる。
eval.pl

#!/usr/bin/perl
use strict;
use warnings;
use Date::Simple qw/date today/;
my $input = $ARGV[0];
eval{
        my $date = date($input); # ここで例外が発生すると予想
        print $date, "\n";
};
warn "Warning: ".$@ if $@; # 例外をトラップすると想定
print "--end--\n";
exit;

Date::Simple的にあり得ない日付データ(例えば「20091000」とか)を渡す。

% perl eval.pl 200910000
Use of uninitialized value in print at eval.pl line 9.

--end--

dateオブジェクトを生成する部分ではなく、print部分でのエラーになってるが処理自体は最後まで行われているようだ。
うーん。オブジェクトが生成できなかったのは「例外」じゃないってことなんだろうか。

というか、そもそも例外って何だろう?

先ほど参考にさせていただいたサイトから引用。

例外は少し変わった形のエラー処理機構です。


通常、何かの処理を行うとき、それが失敗したら、エラー時の処理を呼び出すことになります。このとき、関数がエラーを表す値を戻り値として返す実装が一般的です。Perl の場合、値を伴わない return文を使うことが多いです。返す値を指定しない return文は、スカラーコンテキストでは undef になり、リストコンテキストでは空のリスト () になるので、処理が成功しなかったことを示すのに都合が良いためです(もちろん、そうでない場合もあります)。


この方法でのエラー処理の大きな問題は、関数の呼び出し側が忘れずにエラーチェックをしなければならないことです。戻り値は無視してしまえるため、せっかくエラーを表す値を返しているのに、結局、エラーチェックされないかも知れません。すると、ソース上のその場所から、遠く離れた場所で問題が表面化し、突然実行エラーを引き起こすかも知れません。問題の原因の大元は、全然違うところにあるので、デバッグが困難になってしまいます。一般的に、エラーは出来る限り早い段階で検出するべきです。


例外を使うと、エラーが起こった時点で確実に検出できます。実は、例外は既に利用したことがあります。それは die関数です。die関数を呼び出すと、例外を発生させます(例外が発生することを、例外を 送出するといいます)。送出された例外は、どこかで補足 され、その場所で処理されます。ソース上で補足されなかった場合、Perl側で補足し、例外が発生したことをエラーメッセージとして出力して、スクリプトを強制終了させます。単純に die関数を使ったとき、スクリプトが強制終了するのはこのためです。

dieは例外を強制的に発生させる関数ということを理解せずに使っていました。強制的にスクリプトが終了するのが「例外」なのか・・・?まだまだ勉強の余地がありそうです。

同じようなことで悩んでた方が。

参考にさせていただきます。