はじめに
Curlはクライアント開発に特化した本格的なオブジェクト指向言語です。プログラマの使い勝手がよいように、いろいろな処理を自動で補ってくれる便利な機能がたくさんあります。また、ヘルプもかなり充実しています。
ところが、残念ながらこれらのことを実感できるのは、あるハードルを越えた後になります。どの言語も習得するときはそうですが、ハードルを越える前は「何を言っているかわからない」状況になってしまいがちです。実際、筆者自身もCurlの言語仕様を聞いて素晴らしいと感じたものの、悪戦苦闘しているうちに挫折してしまった過去があります。幸い、再度Curlを挑戦する機会に恵まれ、いろいろな方の協力を得て理解することができ、なるほどよく考えられた言語だと実感できるに至りました。
ここでは、そんな自分の経験を生かし、つまづきやすいポイントや、Curlならではの考え方などを紹介していきたいと思います。
対象読者
本連載は、Curlを1度使ってみたことがあるが、いまひとつよくわからない、と感じているプログラマを対象にしています。もちろん、他のJava等のオブジェクト指向言語を使ったことがあり、Curlを勉強されている方も対象となります。
トップレベル
Curlにおいて、なかなか理解しにくいのがトップレベルという言葉です。今回、このトップレベルを説明したいと思います。
トップレベルは名前からも想像できるとおり、ソースコード内の「ある場所」を指し示す言葉です。Curlプログラムは階層的に記述していきますが、非常に大雑把に言うならHTMLで言うところのhtmlタグ、XMLで言うところのrootタグ(最上位の要素)の位置にあたるものがトップレベルになります。
もう少し具体的に説明します。Curlで作成したアプリケーションプログラムは、ブラウザの中で動作するアプレット、もしくは、独自のWindowの中で動作する独立型アプレットのどちらかから起動します。これらのアプレットは、.curlか.dcurlの拡張子を持っています。これらの起動ファイルの中の { } で囲まれていない部分がトップレベルです(図1)。
トップレベルで記述されるのは、テキストもしくはCurlの「式」になります。アプレットと独立型アプレットで動作が異なりますので、おのおのについて見ていきましょう。
アプレット
アプレットのトップレベルには、テキストを記述できます。記述されたテキストは、以下のような簡単なルールにそってブラウザに表示されます。
- トップレベルに書かれたテキストはブラウザに表示される。
- 複数の空白は1つとみなされる。
- テキストを含まない行に続くテキストは新しい段落に表示される。
HTMLファイルを読み込んだブラウザのような動作です。トップレベルのテキストなどの各種表現を行うために、テキスト書式(テキストプロシージャ)と呼ばれるCurlの式が用意されています。文字書式(text)、パラグラフ書式(paragraph)、リスト(itemize/enumerate)、テーブル(table)、リンク(link)、イメージ(image)などがあります。
もう1つ、アプレットのトップレベルには、Curlの式が記述できます。トップレベルに書かれた式を「トップレベルの式」と呼びます。式には、CurlのコマンドかCurlの一連の処理が書かれたコードブロックが記述されます。
変数を定義するサンプルコードを示します。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} {let a:int = 100} || (1) トップレベルで定義された変数 {do let a:int = 200 || (2) コードブロック内で定義された変数 let b:int = 100 || 同上 } a = {value a} b = || {value b} || エラー
(1)は、トップレベルの式でCurlのコマンドが実行されています。この変数はトップレベルで実行されたとみなして、変数はトップレベル以下ならどこでも利用できる変数(グローバル変数)になります。
(2)は、コードブロックの中でCurlのコマンドが実行されています。コードブロックには、スコープの範囲となります。そして、コードブロックの中はトップレベルの下のレベルになります。コードブロック内で定義された変数は、定義されたコードブロックより下のレベルで有効な変数(ローカル変数)となります。
ここで、コードブロック内で、let/set/defを使う場合は、{ } で囲まないのが作法になりますので注意してください。囲ってもエラーにはなりませんが、囲まないことが推奨されています。トップレベルの式では、Curl式であることを示すために、{ } で囲まなければなりません。
まとめると、アプレットでは、テキストを描画する領域であるデフォルトビューがブラウザの中に用意されます。そこに、トップレベルで書かれたテキストが表示され、トップレベルの式の戻り値が表示されることになります。
独立型アプレット
次に、独立型アプレットです。独立型アプレットでは、自分で描画の土台となるビューを用意する必要があります。明示的にビューを生成して、そこに表示させます。IDEで自動的に生成されるコードは以下のようになります。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} {View || この{Frame}を置き換えて使用してください {Frame width = 8cm, height = 8cm}, visibility = "normal", {on WindowClose do {exit} } }
このコードのトップレベルにテキストをどうなるか書いてみましょう。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} 独立型アプレットでテキストを書きました。 {View || この{Frame}を置き換えて使用してください {Frame width = 8cm, height = 8cm}, visibility = "normal", {on WindowClose do {exit} } }
実行しても、エラーにはなりませんがテキストは何も表示されません。アプレットと同じようにトップレベルのデフォルトビューに書いたとしても、そのビューは表示されませんので、結果として何も表示されないのでしょう。
ビューを生成して表示するためには、プログラマがViewを生成するCurlのコードを書く必要があります。コード内はコードブロックになりますのでトップレベルではありません。独立型アプレットでは、トップレベルのテキストは機能しないことになります。
コードブロック
Curl式は、コマンドかコードブロックであると書きました。どちらも、{ } に囲まれています。{ } がコードブロックの範囲かと言うとそうとも限りません。コードを書いていると経験的にはわかってきますが、コードブロックについて具体的な事例で説明します。
トップレベルでコードブロックを記述するのに利用するvalueマクロのサンプルです。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} {value || コードブロック開始 let a:int = 10 {inc a} a || コードブロック終了 }
valueは、トップレベルの式です。その中のインデントされている部分がコードブロックになります。その中で変数のスコープが有効になります。
if式の例です。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} {let a:int = 10} {if a < 20 then || コードブロック開始 let b:int = a + 10 set a = b || コードブロック終了 else || コードブロック開始 let b:int = a - 10 set a = b || コードブロック終了 } a = {value a}
if式全体が1つのコードブロックなのではなく、thenの後、elseの後で2つのコードブロックが形成されます。この2つに同じ名前の変数を定義してもエラーにならずに利用できます。switch文などでも同様なことが言えます。
プロシージャ定義の例です。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} {define-proc public {pythagoras a:double, b:double}:double || コードブロック開始 let c:double = {sqrt a * a + b * b} {return c} || コードブロック終了 } {pythagoras 5, 12}
プロシージャ定義は、define-procで始まっています。この中はコードブロックです。内部で定義した変数はローカル変数です。
トップレベルの実行順番
トップレベルで記述されたテキストや式は、上から順番に実行されます。プロシージャなどを定義する場合は、プロシージャを呼び出すコードよりも先にプロシージャが定義されている必要があります。
{curl 7.0 applet} {curl-file-attributes character-encoding = "shift-jis"} {define-proc public {pre-proc a:int, b:int}:int {return a + b} } {pre-proc 1, 2} {post-proc 3, 4} || エラーになる {define-proc public {post-proc a:int, b:int}:int {return a + b} }
実行するとこのような結果(図2)になります。
前もって定義したプロシージャは呼び出すことができますが、後ろで定義したプロシージャは見つからずにエラーとなります。クラスなどでは、メソッドの位置によって実行できないなどのようなことは発生しませんのでご安心ください。
最後に
トップレベルとコードブロックについて見てきました。Curlのプログラムを書く場合、独立型アプレットを使ったり、アプレットでもクラスやプロシージャの実装が中心になると思います。結果的に、トップレベルはあまり利用しないのですが、Curlの理解に少しでもお役に立てれば幸いです。