F#でインタープリタ : オブジェクト指向

クラスの定義とインスタンス化ができるようになりました。

    class Position {
      x = 0
      y = 0
      def move(nx, ny) {
        x = nx
        y = ny
      }
    }

    class Pos3D extends Position {
      z = 0
      def set(nx, ny, nz) {
        x = nx
        y = ny
        z = nz
      }
    }

    p = Pos3D.new
    p.move(3, 4)
    print(p.x + p.y)
    p.set(5, 6, 7)
    a = p.x + p.y + p.z
    print(p.x + p.y + p.z)

このスクリプトを実行すると7と18を出力します。


当初の目標どおり、今のところmutableな型を使わずにがんばっていますがだんだんと難しさも感じてきました。

値を共有しないことの不便さ

実装がいまいちなのか、慣れていないからか、永続データで値を共有しないことによる不便さがあります。
ある値が共有されている場合、その値を変更して共有元に追随させたいときに難しいです。
たとえば、次のようなコードでグローバルスコープでオブジェクトを作ってフィールドに値を代入した場合です。

p = Pos3D.new
p.x = 10

上のスクリプトに対応するインタープリタの実装部分は次のようになっています。
共有している部分を全部作り直さないといけないのが大変。

  let assign env lhs rhs cont =                   (* envがグローバルな環境 *)
    match lhs, rhs with
    | Var(id, _), v -> 
      let newEnv = Env.add id v env
      cont newEnv v
    | Member(objId, memberId, Obj(objEnv)), v ->  (* objIdがp、memberIdがx、objEnvがpの環境、vが10に対応 *)
      let newObjEnv = Env.add memberId v objEnv   (* pの環境を再作成(xを10に更新) *)
      let newObj = Obj(newObjEnv)                 (* pに新しい環境を設定 *)
      let newEnv = Env.add objId newObj env       (* グローバルな環境を再作成(新しいpを参照) *)
      cont newEnv v                               (* 新しいグローバルな環境で後続処理 *)
    | _ -> failwith (sprintf "%A is not variable." lhs)

相互参照の問題

永続データ型と判別共用体で相互参照できなくて悩みます。たとえば、this参照。

(* オブジェクトを表す判別共用体に環境を表す永続データを持たせる *)
let newObj = Obj(newObjEnv)
(* 環境にオブジェクトを追加してthisで自身を参照できるようにしたいが、無理。thisで取得できるのは古い環境をもったオブジェクト *)
let newObjEnv = Env.add "this" obj newObjEnv      

特にこれといった解決策がなくて、どうすべきか悩み中。