2016-05-26-01-ファンクタをfuncallableにする問題について-2 - project-enigma

2016-05-26-01-ファンクタをfuncallableにする問題について-2

>> Site top >> weblog >> 月別アーカイブ >> 2016年05月のlog >> 2016-05-26-01-ファンクタをfuncallableにする問題について-2

最終更新日付:2016/05/26 11:25:39


ファンクタをfuncallableにする問題について-2

2016 年 05 月 26 日

前回、ひとまずはやる価値ありと判断したファンクタの funcallable 化問題。今回は実際にどんな修正をすることになるかを整理する。前回書いた内容に対しては、「このへんが参考になるよー」といった情報を Twitter 経由で頂いた。感謝致します。

さて、試行錯誤の結果、functor クラスは従来通りとなった。つまり以下。

(defclass functor (clonable)
  ((closure :type     cl:function
            :initform nil
            :initarg  :closure
            :accessor __functor-closure)))

 

また、0x11 以降は deprecated となっている2つのクラスも従来通り functorから派生する。

(defclass unary_function  (functor) ())
(defclass binary_function (functor) ())

 

手が入るのは、functor または上記2クラスのいずれかから派生するファンクタの具象クラスだ。たとえば、plus なら以下のようになる。ご覧の通り、closer-mop を利用することになる。

(defclass plus (#-cl-stl-0x98 functor
                #+cl-stl-0x98 binary_function
                closer-mop:funcallable-standard-object)
  ()
  (:metaclass closer-mop:funcallable-standard-class))

 

これを全てのファンクタクラスについてやるのはちょっとアレなので、以下の簡単なラッパーマクロ define-functor を用意する。

(defmacro define-functor (name (&rest superclasses) (&rest slot-specifiers) &rest class-options)
  `(defclass ,name (,@superclasses closer-mop:funcallable-standard-object)
     ,slot-specifiers
     (:metaclass closer-mop:funcallable-standard-class)
     ,@class-options))

 

これで、前述の plus は以下のように書ける。事実上、これは既存コードの defclassdefine-functor に書き換えるだけの作業だ。

(define-functor plus (#-cl-stl-0x98 functor
                      #+cl-stl-0x98 binary_function) ())

 

functor に戻るとしよう。initialize-instance:after メソッドにおいて set-funcallable-instance-function をする必要がある。

(labels ((empty-fnc (&rest args)
           (declare (ignore args))
           (error 'bad_function_call :what "empty function.")))
  (defmethod initialize-instance :after ((fnctr functor) &key)
    (let ((closure (__functor-closure fnctr)))
      (if (null closure)
          (setf (__functor-closure fnctr) #'empty-fnc)
          (closer-mop:set-funcallable-instance-function fnctr closure))))
  (defmethod (setf __functor-closure) :after (closure (fnctr functor))
      (if (null closure)
          (setf (__functor-closure fnctr) #'empty-fnc)
          (closer-mop:set-funcallable-instance-function fnctr closure))))

 

登録する関数が nil になることは基本的にないのだけれど、stl:function の対応のためだけにこれをサポートする必要が生じている。いわゆる「空」のstl:function をコールすると、bad_function_call 例外が投入されるので、そのためのコードだ。(setf __functor-closure):after メソッドでは、設定される関数が nil だったら代替関数を再度 setf するという、なんだか気持ち悪いコードになっているが、他に方法を思いつかなかった。

‥‥‥というわけで、とりあえずはこんな方式になりますよ、という話。あと、functor-call は廃止するつもりだったのだけれど、単純に funcall に展開されるマクロにしておき、deprecated とする方が良いかな、と考えている。

実はコードの修正は完了していて、テストも通っている。とはいえある程度大きな修正なので、しばらく様子を見たいというのが正直なところ。まぁ、なんていうか、Lisp meetup でも話したことではあるのだけれど、ラムダ式が自在に使える Lisp の世界ではクラスとしてのファンクタは無用の長物。今回の修正も、あまり使われることはないのだろうな、と思うとちょっぴりさびしいね。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : STLとその移植

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.