global 変数の(僕にとっては)奇特なスコープ(変数の有効範囲)について、自分の覚え書きという意味でもまとめてみたいと思います。
僕は長い間、AppleScript の global 変数とは「初期値を持たない property だ」とずっと思っていて、そんな使い方しかしていなかったのですが、全然違うものです。そして、それはある意味すさまじく危険なもの(うまく使えば強力なもの)であります。僕と同じ勘違いをしていらっしゃる方は少なくないと思いますが、お気をつけください。property と global 変数どちらを積極的に使うかといえば、特に理由がない限り property を使うべきだと思います。理由は追々分かるかと思いまが、とくに、ライブラリ的に使う Script Object の中では、特に理由がない限り使うべきではないと思います。global 変数には global 変数の役割がありますから。
というわけで、global 変数のスコープについて解説を試みます。まずは、global 変数のそれと比較するために、local 変数と property のスコープについて簡単に書いておきます。
いたってシンプルです。基本的に handler の中だけです。特になんの宣言もなしに変数を使った場合は、この local 変数になります。
もし、run handler が定義されておらず、スクリプトの top level で宣言のない変数を使った場合、その変数は自動的に暗黙の run handler の local 変数になります。
property もいたってシンプルです。property は script object の top level でしか宣言できず、その script object 内だけで有効です。また、その script object 内で定義された script object にもそのスコープは及びます。例えば、外部のファイルから load script コマンドを使って load された script object にはスコープは及びません。
内包する script object に同じ変数名の property が宣言されていた場合、そこで、より上位の property のスコープはブロックされます。
上記の local 変数のスコープと property のスコープは直感的で、誰でもそうかと思うものだと思うのですが、 global 変数は違います。global 変数の何がすごいって、global 変数は宣言されたところより、より上位のレベルにもそのスコープが及ぶのです。むやみやたらに上位レベルを侵略するわけではなく、top level の run handler の local 変数と property を狙い撃ちします。
以下のようなスクリプトが典型的な global 変数の使い方でしょう。ある特定はhandler間で変数を共有するのに使っています。
このように、global 変数 theValue は、handler showValu eの中にその魔の手を伸ばしておりませんが、run handler の中には global 変数 theValue のスコープは及んでいます。
top level の run handler の中で、global 変数の侵入を防ぐには、次のように明示的に local 変数を宣言してやる必要があります。
run handler の中なら、どれでも global 変数のスコープが及ぶというわけじゃなくて、top level の run handler が狙い撃ちされます。次の例を見てください。
このように、top level の run handler の中では global 変数 theValue を参照できますが、script object Joe の中の run handler では global 変数 theValue を参照することはできません。
また、global 変数は top level の run handler だけでなく、top level に同じ変数名の property が定義されていると、その内容は global 変数と共有されてしまいます。
上記の例のように、script object Donna の property には、その内側で宣言されている global 変数 theValue の影響は及びませんが、top level の property にはその global 変数と値を共有します。
素朴な疑問として、外部のファイルから load した script object の場合はどうなるのか?と思われるかと思います。僕が試した限りでは、
load した script object の内部まで、global 変数のスコープは及ばないようです。
load する側のスクリプトが最終的 top level であるかどうかが問題なのですが、とにかく load した script object の中に global 変数があったら、その global 変数は最終的な実行時に top level になる run handler や property の中の同じ変数名のものを探しに行きます。ここまでは、これまでの例のようにスクリプト内に script object を書いた場合と同じなのですが、違うのは load した script object の中の run handler の中も global 変数に含まれてしまうことです。
example3 で示したように、スクリプト内に script object を書いた場合、その script object の run handler は global 変数のスコープに含まれるとは限りません。しかし次の例のように外部のファイルから load した script object の場合その run handler は global 変数のスコープに含まれます。
まず、example3 でスクリプト内に script object として書いた以下のスクリプトを別のファイルとして保存します。
そして、このスクリプトを load するスクリプトの例です。
この結果は、example3 とは違います。example3 の場合、最後の script object 「Joe」の run handler の実行部分ではエラーが起きていましたが、今度は"hello"という結果が返ってきます。
すなわち、ファイルの top level にある run handler が global 変数のスコープに自動的に含まれるということです。これは property に関しても同じ様です。
AppleScript 言語ガイドに書いてあるわけじゃないのですが、ファイルの top level にある run handler 内では明示的に local 変数として宣言されてない限り、変数は global 変数らしいのです。
まず証拠として、良く知られていることですが run handler 内の変数の値はスクリプト終了後も保持されることです。これは global 変数とproperty の性質のはずです。
そして、local 変数として明示的に宣言すると、保存されなくなります。
また、example1 のように、run handler 内が勝手に global 変数のスコープに含まれてしまうことも、そう考えると納得がいくのではないでしょうか。
基本的に global 変数は同じ変数名が定義されているところがスコープになるわけですが、それ以外にも、スクリプトの top level とファイルの top level の run handler と property がそのスコープに含められているということですね。
script object を利用したオブジェクト指向をせずに、フラットなスクリプトを書く分なら global 変数はそれほど恐れるべきものでもありませんが、script object を組み合わせるような方法でスクリプトの開発を行っていると気をつけなければなりません。いつの間にか、より下位の script object の中で使っていた global 変数と最上位の property が共有されていたとか、最上位の run handler 中で、下位の script object の global 変数をいじってしまった、何てことが起きかねませんから。
しかし、実は global 変数は複数の script object を駆使してオブジェクト指向しているスクリプトでこそ使い道があります。詳細は、拙文「AppleScript ソースコードの分割と共有」をご覧になってください。
global 変数の使用について警告を促しておきながらも、誤解無いように申し上げておきますが、僕は AppleScript には global 変数の取り扱いにbugがあるといっているわけではありません。僕の見るかぎりあらゆる点で、言語ガイドと矛盾は無いですし、ただその振る舞いが、(僕にとっては)複雑で(多分、自分を含めた多くの人にとって)理解しにくいだけだと思います。実際、僕も何年にも渡ってよくわかっていなかったですし、気にも止めませんでした。という訳で、その覚え書きですから。
global 変数の面白い性質として、ある script object の中で global 変数が定義されているとして、その script object を copy によって複製すると、その global 変数は複製された script object 間で共有されます。copy によって複製された script object 間では、global 変数を通じて値をやりとりできるのです。いわゆるクラス変数の代用になるかと思います。
また、「AppleScript ソースコードの分割と共有」で詳しく説明している通り、load script した二つの scirpt object 間でのメッセージのやり取りをする際にも、global 変数を使う必要があります。
何か、間違っていることや不適切なことをがあれば、御指摘いただければ幸いです。