Smalltalk-72で学ぶOOPの原点:条件分岐式についてもう少し
アラン・ケイの“オブジェクト指向”というアイデアをもとに(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72で遊んでみるシリーズです(なお最新のSmalltalkについては Pharo などでお楽しみください!)。他の記事はこちらから→Smalltalk-72で学ぶOOPの原点 Advent Calendar 2019 - Qiita
(配列要素の参照と要素の代入の実装からの続き)
Smalltalk-72の条件分岐式
第三世代目のSmalltalkであるSmalltalk-80以降(今のSmalltalkを含め)では、条件分岐もメッセージ式で表現することが比較的よく知られています。次の式は、今のSmalltalkで、「3 < 4 ならば 5 を、そうでなければ 6 を返す」という式です。
3 < 4 ifTrue: [5] ifFalse: [6]
3
に < 4
というメッセージを送った結果の true
に対して ifTrue: [5] ifFalse: [6]
というメッセージが送られる(と解釈する)ことで条件分岐を表現しています。
一方で、Smalltalk-72は制御構造としての条件分岐式 ⇒()
を用意しています。通常のメッセージと同様にトークンに分解されメッセージの一部となりますが、レシーバーが解釈する前に処理系によっていわば予約語として特別扱いされ処理されます。
3 < 4 ⇒ (5) 6
ALGOLライクなif-then-elseの実装
Smalltalk-72の条件分岐式に関連して、ブートストラップコードであるALLDEFSを読んでいるとこんな定義が登場します。
コメントにもあるようにこれはALGOLライクな、つまり、通常の言語の条件分岐式とよく似た記法を、その意味するところは冒頭の if
というアクション(オブジェクト)に対して、偽・非偽値(を返す式)、then
、非偽時の処理、else
、偽時の処理 といったもろもろの必要な情報をすべてメッセージとして送りつけることにより記述可能にするという実に驚きのアプローチです。
定義を細かく見てみましょう。
to
は「クラス」もしくはインスタンス生成能を持たないクラスである「アクション」を定義するためのオブジェクト(これ自体もアクション)です。ここでは if
というアクションを定義していて、続く exp
はこのアクション内で用いられる一時変数の宣言です。その先に続く括弧内のがメソッド(つまり、if
に送られてきたメッセージにどう対処するかを記述したコード)で次のような手続きが記述されています。
- メッセージの冒頭からのトークン列を式とみなして評価し結果を
exp
に代入し、その値がもし非偽ならば…(:exp⇒(
)★ - 続くトークンが
then
にマッチしたらあらためて続くトークン列を式として評価して結果をexp
に代入(∢then⇒(:exp.
)☆ - 続くトークンが
else
にマッチしたら続きからトークンをひとつ、評価せずに消費(:☞.
)。マッチしてもしなくてもexp
を返す。(つまり、else節は省略可) - ☆でなければ
then
が無い旨をエラー。(error ☞(no then)
) - ★でなければ、続くトークンが
then
にマッチしたら続きからトークンをひとつ、評価せずに消費(∢then⇒(:☞.
)。▲ - 続くトークンが
else
にマッチしたら続くトークン列を式として評価して結果をexp
に代入して返す(∢else⇒(:exp)
)。△ - △でなければ
false
を返す。 - ▲でなければ、
then
が無い旨をエラー(error ☞(no then)
)。
ためしに if 3 < 4 then 5 else 6
を評価してみましょう。
たしかに動きますね。ただし、この定義だと偽値時に実行されない then節、あるいは、非偽値時に実行されない else節が式の場合、括弧でくくらないとうまく処理できなさそうです。実際に試すとたしかにエラーになります。
then節、else節にリテラルをひとつだけ書くのでなければ括弧でくくる運用が必要そうです。
ちなみに、トークンを評価せずに取り込む :☞
は初出ですが、ALLDEFSの :
アクションの定義( to : …
)に擬似コードがありますので参照してください。
Smalltalk-72の条件分岐式の落とし穴
<偽値・非偽値> ⇒( <非偽時処理> ) <偽時処理>
のうち、括弧でくくられる非偽時処理だけでなく、偽時処理にも複数の式を書くことが出来ます。従って、条件分岐の後にもコードを続けるときには、この条件式全体を括弧でくくらないと、期待した動作にはならないので要注意です。
3 < 4 ⇒(5 print) 6 print. 7 print. '=> 7 は出力されない(偽時処理に含まれるため)'
(3 < 4 ⇒(5 print) 6 print.) 7 print. '=> 7 も出力される' (3 > 4 ⇒(5 print) 6 print.) 7 print. '=> 7 も出力される'
(条件分岐以外の制御構造に続く)