Gaucheで作業していると、しばしば未定義値 #<undef>
に出会うことがあるでしょう。
gosh> (if #f #t) #<undef>
この値は、値そのものに意味が無いこと、 ほかにふさわしい値が無いこと、あるいは束縛する値がまだ計算されていないことを示します。
未定義値と未束縛の変数を混同しないようにしてください。
#<undef>は通常の第一級の値であり、変数に束縛しておくこともできます。
未束縛の変数は文字通り、変数が束縛されていない、すなわち値を持たないことを意味します。
しかし、場合によっては、変数に特定の値が提供されていないことを
示すのに#<undef>が使われることもあります。
例えば、トップレベル変数は、(define variable)の形式で定義された場合に
#<undef>に束縛されます(定義参照)。
また、既定値をもたない省略可能引数に実引数が提供されなかった
場合、引数の値は#<undef>となります (手続きを作る参照)。
これは実際に#<undef>が初期値として、あるいは引数として渡された場合と区別がつかない
ことに注意してください。#<undef>を受け取ってわかることは、
せいぜいその値にたいした意味がないということくらいです。
#<undef>に大きな意味を持たせすぎないようにしましょう。
#<undef>は一般化された真偽値としては真の値とみなされます
(偽になるのは#fのみですから)。
しかし、#<undef>の値を見て分岐するコードは危険です。
というのも、戻り値が規定されていない手続きが暫定的に#<undef>を返している
場合が良くあるからです。でも、それは将来変更されるかもしれません。
戻り値が規定されていない以上、戻り値に依存するコードは無いはずだからです。
もしうっかり、その戻り値に基づいて分岐しているコードがあると、変更があったときに
壊れてしまいます。
実のところ、#<undef>に依存して分岐しているコードが結構多いことがわかりました。
これは将来のバグの芽となるので、#<undef>が分岐テストに現れた時に
警告する機能がつけてあります。環境変数GAUCHE_CHECK_UNDEFINED_TEST
をセットすることでその機能をオンにできます。
将来、テストの際にこの機能をオンにするかもしれません。
#<undef>をうっかり分岐テストに使ってしまう良くあるパターンは、
and-let*です。次のコードでは、printがテスト節に現れていますが、
このコードはprintが常に#<undef>を返しそれが真の値であることから
次のテスト節に進むことを期待しています。けれども、printが場合によっては#fを
返すように変更されたら、このコードは壊れてしまいます。
(and-let* ([var (foo x y z)]
[ (print var) ] ;; branch on #<undef>
[baz (bar var)])
...)
以上の注意を念頭においた上で、未定義値を扱うには 次の手続きが利用できます。
objが未定義値である場合に限り#tを返します。
未定義値を返します。