増6.「単一代入」考
●今回の発端
「自転車とプログラミングと」の、「7.最後に」で、“ある範囲内では何度でも代入でき、その範囲を超えたら(あるいは超えたことを示す単一代入の属性をセットしたら)、絶対にいじれない”ように緩和された「単一代入」が有ったらいいという趣旨の発言をしました。
2~3、指摘も受けましたが、自分なりの理屈はあったのです。
今回は、それについて弁明します。
●「単一代入」
筆者の若かりし頃、「人工知能」だとか「第五世代コンピュータ」だとかが人口に膾炙していた時期がありました。
そこで使われていた言語にPrologというのがあり、“変数は一度作ったら値を二度と変えられない”という特徴がありました。
現在でも、言語によっては(文字列型などで)その様な仕組みのあるものもあるとは思いますが、Prologはもっと徹底していて、変数への参照の変数がなく、.Netなどで当たり前にできる、
a String
a ←(参照を代入) new String("foo") -(1)
a ←(参照を代入) new String("bar") -(2)
((1)で作った"foo"は、(2)では壊され新たに"bar"が作られる。変数の参照への変数は何度でも変えられる)
のといったなこともできませんでした。
●理屈の背景
筆者は、PHPでホームページを作っています。他の言語は知らないのでここではPHP5.3限定で、ホームページを作る際の問題について書きます。
PHP5.3は、
- グローバル変数(スカラー変数、オブジェクト変数への参照)の生存期間が「そのページ限り」で、それを超えた生存期間が必要な場合、セッション変数を使います。
- セッション変数には、オブジェクト変数への参照をセットできません。
- シリアライズすれば、オブジェクト変数をセッション変数にセットできます。
という制限があります。
筆者は3番目の「シリアライズ」とを知りませんでした。馬鹿ですが、残念ながら事実です。つまり、画面限りでない変数はすべて、スカラー変数としていました。
あと、ここで言うPHPのホームページは、ボタンなどを押すと必ず画面遷移する形式のもので、新式の「ページを固定して、innerHTMLを順次書き換える」ものではありませんでした。
●その結果なにが起こったか
まさに混沌です。
●なにかできないか
オブジェクト/クラスによる整理ができず(本当はできたのですが、しませんでした)、パッケージ的なまとめ上げる言語機能も知らない状況で(自分だけ知らないのかもしれませんが、PHP5.3にはなかったように思います)できることを考えました。
- 個々のセッション変数は、特定の画面PHPファイル、機能PHPファイル内でのみ変更し、他では変更しない。
という規範です。それを担保するために、
- セッション変数のキー(変数名)に接頭語をつけ、どの画面/機能が“本籍地”か
を区別しました。“本籍地”以外では値を参照するだけにとどめます。
言語・機構等で強制できない、単なる規範ですが、特定の画面/機能ファイル内を観測できない存在から見ると、雰囲気「単一代入」に見えなくないと思います。
●よかったこと
ボタンを押すたび画面遷移する形式のPHPでは、ブラウザの「戻るボタン」が大敵です。一度通った画面をブラウザの「戻るボタン」で戻られると、戻った先の画面ではキャッシュされた画面情報が表示され、PHPの機能が実行されることはなく、
- 画面1でセッション変数1の値に1をセット
- 画面2に遷移
- 画面2でセッション変数1の値に2をセット
- ブラウザの戻るボタンで画面1に遷移
- 画面1でセッション変数1に2が入っている
というシナリオで、もし画面1でセッション変数1に2が入っていると問題になる場合、困ってしまいます。
たとえ、画面2上に“PHPで管理している「戻るボタン」”が存在し、そのボタンを押下すると
- セッション変数1を1に戻してから画面1に遷移
の意図だったとしても、ブラウザの「戻るボタン」によって、その処理は無視されてしまいます。
(jQueryのReadyイベント(ブラウザの「戻るボタン」でも起きる)でも使って、AjaxでPHPの所定の機能をキックすればいいかもしれませんが、そのようなことは当時思いつきませんでした。)
それが、今回の「単一代入」では起きなくなるのです(画面2でセッション変数1に値をセットする事は禁止だからです)。ブラウザの戻るボタンは単純に考えると、Prologのバックトラックと似ている点があると思います。ですので、Prologで取り入れられた「単一代入」が、PHPでも(いくらかでも)寄与するのだと思います。
●オブジェクト/クラスなら解決か?
オブジェクト/クラスを使うと、特定の値の保持や操作をその中に集中して納めることができ、上記の様な問題はなくなると思われがちです。しかしながら、オブジェクト/クラスであっても、
- 状態を更新するメソッドを(複数の画面などで)制限なしに利用する
ならば、結局、混沌になると思います。
オブジェクト/クラスにしただけでは、値や操作を1つの場所にまとめることはできても、その利用をまとめたことにはならないのです。
値や操作の利用までを簡単に管理する常套手段として、ReadOnlyでない部分に着目する方法がありますが(RDBの分野でのCRUD図など)、もっとも単純な(よりきつい規範として)「単一代入」はよい方法の1つだと思います(後知恵ですが、「シリアライズできるのは“本籍地”の画面/機能PHPファイルのみとし、シリアライズするまでのオブジェクトは何度も更新するが、一度シリアライズしたオブジェクトは二度と更新しない」と言ったやり方もあるかと思います。)。
「7.最後に」で筆者の念頭にあったのはこういうことでした。と筆者は弁明します。
まぁ、今回はこんな所です。
●コラムのコメント欄の方針
コメントに対し、当意即妙の回答を、それなりのタイミングでする自信がありません。
ですので、このコラムで、
・わたしは基本的にコメントに答えない
・コメントを書く人は、回答がない前提で議論を進めていただく
とさせていただきます。