SKILL++に移行しましょう

SKILL++と言うと、よく知らない人は、オブジェクト指向SKILLだ〜、とさらによくわからなくなるような事を言うのだが、単にSchemeをベースにしたSKILLと言うだけのことです。オブジェクト指向とかは、タダの付録なわけですね。

ちなみにSKILLオリジナルは、Common Lisp以前のFrantz Lisp(って言うんでしたっけ?)をベースにした言語です。

さて、このSKILLとSKILL++の最大の違いは、先のオブジェクト指向云々の問題ではなく、スコープが異なっていることです。

  • SKILL: ダイナミックスコープ
  • SKILL++: レキシカルスコープ


それぞれの言葉の意味は、On Lisp辺りを見てもらうことにして、ここでは、実際にどういう差があるか、と言うのを見ていきます。

ダイナミックスコープはローカル変数を大域環境で書き換えることができる

次のSKILLプログラムは、あるフォームを表示させて、ユーザの入力を待つものです。

(defun mySkillFunc ()
 (let (listBox form (mes "hoge"))
  (setq listBox (hiCreateListBoxField ?name (gensym) ?choices nil))
  (setq form (hiCreateAppForm ?name (gensym) ?fields (list listBox) ?buttonLayout 'OKCancel))
  (hiDisplayForm form)
  (println mes)))

ここで、mesと言うローカル変数を作っておきます。"hoge"と言う値で初期化しておきます。

さて、こいつをCIW(Command Interpreter Window)にloadさせて、実行させたときの結果が以下です。

\i (load "skill.il")     ; SKILLプログラムをロード
\t t                     ; ロード完了
\i (mySkillFunc)         ; SKILL関数実行 -> フォームが立ち上がり、ユーザ入力待ちになる。
\i mes                   ; 大域環境から、mesと言う変数にアクセスしてみる。
\t "hoge"                ; えっ、見えるの? ( ´・ω・`)
\p > 
\i mes = "hage"          ; んでは、試しにmesを書き換えてやろう。
\t "hage"                ; お前は"hage"だ〜。
\p >                     ; フォームをクローズ。
\o "hage"                ; 元のSKILLと関数内の(println mes)の副作用。えっ、値が変わってるΣ(゚д゚) 
\t nil

と言う感じで、内部でローカル変数を使っていても、実行時にダイナミックに値を変更することができ、さらに、変更したことにより、プログラムが影響を受けてしまいます。

これは非常に危険です。ユーザの入力を待っているSKILLプログラムがいる状態で、他のSKILLプログラムがたまたま値を書き換えてしまうことで、容易に再現しづらいバグを引き起こしてしまいます。

一方、レキシカルスコープのSKILL++ではどうだ?

同じプログラムを拡張子".ils"で保存すると、CIW君は自動的にSKILL++のプログラムとしてロードしてくれます。

では、CIWの応答を見てみましょう。

\i (load "skill.ils")           ; SKILL++のプログラムとしてロード
\t t
\i (mySkillFunc)                ; フォームを立ち上げる。
\p >
\i mes                          ; さて、mesの値は。。。トップレベルにはそんなシンボルがない!!
\e *Error* toplevel: undefined variable - mes
\p >
\i mes = "hage"                 ; では、トップレベルにmesと言うシンボルを作ってやる!!
\t "hage"
\p >                            ; フォームをクローズ。さて、mesは影響を受けているか?
\o "hoge"                       ; 元の値"hoge"が表示されてる(^ω^)
\t nil
\p >
\i mes                          ; グローバルのmesはあいかわらず"hage"だ。
\t "hage"
\p >

というような感じになり、おそらくはプログラマが意図した通りの結果になります。

SKILLを書く人間

最近、よく感じることですが、SKILLを書いている人間は、このような事を知って書いているのでしょうか? おそらくは知らないでしょう。ひょっとすると、元がLispであることすら知らずにコーディングしている人もいる気がします。

そして、最近のプログラミング言語はレキシカルスコープを採用しているのがほとんどなので、このような現象にぶち当たっときには、慌てふためくことになるのでしょう。

ということで、最近のプログラマはSKILLよりもSKILL++に移行した方がよいでしょう。理由はオブジェクト指向が使えるから、というものや、変数と関数の名前空間が同一である、とかそういうコトではなく、このスコープの一点につきます。

普段、レキシカルスコープの言語に慣れている人間から見ると、ダイナミックスコープの言語の挙動は予想がつかないことが多いです。その予想がつかない挙動に悩まされるぐらいだったら、SKILL++に移行してしまった方がよいでしょうね。