2014-04-06-01-マクロと総称関数で関数オーバーロードを真似る - project-enigma

2014-04-06-01-マクロと総称関数で関数オーバーロードを真似る

>> Site top >> weblog >> 月別アーカイブ >> 2014年04月のlog >> 2014-04-06-01-マクロと総称関数で関数オーバーロードを真似る

最終更新日付:2014/04/06 23:50:00


マクロと総称関数で関数オーバーロードを真似る

2014 年 04 月 06 日

Common Lisp の総称関数は素晴らしい。が、不便だと思う部分がないわけでもない。そのひとつが、C++ でいうところの関数オーバーロードができない(っぽい)ところだ。関数オーバーロードであれば引数の数からして全然違うような同名の関数を定義できるが総称関数の仕組みではできないように見える。今回は、これについて実験してみたことについて少し。

 

ひとまずのところ、オーバーロードしたいのが2種類程度で引数の数も1つしか違わないのであれば、その1つを &optinoal にしてしまえば良い。しかし、最低1つ、最高で4つとかあるような6種類のオーバーロードをしたい場合はどうなるだろうか。&rest 引数にするとパラメータの特定化ができないので、その時点で困ってしまうわけだ。

最初にこの問題に行き当たったのは、例によって STL の Common Lisp 移植である CL-STL でだ。コンテナのコンストラクタには何種類ものオーバーロードがあるが、最初はその種類によって異なる名前をつけていた。それがどうにも不格好に見えて嫌だったわけだな。そこで、次のような仕組みをひねり出してみた。

まず、オブジェクトの作成には new というマクロを統一的に使うことに決めた。そのマクロ定義は以下のようなものだ。

; new macro ( calling constructor )
(defmacro new (type (&rest args) &optional (package :cl-stl))
  (labels ((local-symb (&rest args)
             (values (intern (apply #'onlisp/mkstr args) package))))
    (case (length args)
      (0 `(,(local-symb "__NEW-" type "-0") ,@args))
      (1 `(,(local-symb "__NEW-" type "-1") ,@args))
      (2 `(,(local-symb "__NEW-" type "-2") ,@args))
      (3 `(,(local-symb "__NEW-" type "-3") ,@args))
      (t `(,(local-symb "__NEW-" type "-N") ,@args)))))

 

内部で labels している local-symb は、On Lisp にある symb 関数の変形で、やはり On Lisp の mkstr をコールしている。CL-STL は自己完結させたいライブラリなので、mkstr は内部で onlisp/mkstr という関数にしている。で、local-symb はパッケージ名を取って intern に渡すところだけが On Lisp の symb と違う。前提はこれだけだ。

で、このマクロはタイプ名とコンストラクタに渡すパラメータを取る。最後の &optional な package はデフォルトで :CL-STL になる。これは基本的に CL-STL のためのものだからだが、望めば同じ仕組みを使う他パッケージのオブジェクト作成にも使える。で、このマクロの展開は以下のようになる。

(stl:new vector (10 'FILL-VALUE))

=> (CL-STL::__NEW-VECTOR-2 10 'FILL-VALUE)

 

要するに、パラメータ数に応じた関数コールに展開してくれるわけだ。それは総称関数かもしれないし、普通の関数かもしれない。同じパラメータ数で複数のオーバーロードがあるのなら、総称関数にして型に応じて特定化すれば良いし、1種類だけならインライン関数にするのも良いだろう。この new マクロによるオブジェクト作成をサポートしたいクラスは、この展開形に沿う内部的なコンストラクト関数を用意すれば良いわけだ。

ちなみに、new マクロの実装ではパラメータ数が3を超える場合は __NEW-FOO-N という形に展開されるが、これはとりあえずの措置だ。このへんをどうするのが良いかはまだ模索中。

そして、マクロにしたことの副作用を忘れてはいけない。当然ながら、関数ではないので他の関数に渡すことはできない。new はそれでもまだ良いが、以下のアルゴリズムで同じ仕組みを採用するのはマズいような気がして迷っている。

transform は昔からあるもので、単一のシーケンスを扱うものと2つのシーケンスを扱うものの2種類が同じ名前でオーバーロードされている。現在 CL-STL では transform1 と tranform2という名前にしているが、これは STL に合わせたいと思っている。そして残りの min とか max は、0x11 で initializer_list を取る形式が追加された。これも min2 とかにしているのをなんとかしたい。

‥‥‥まぁこんなところだ。実際のところ、一番自分をためらわせているのは SLIME での情報表示がわかりにくくなることだったりするのだけれど。

 

コメント

project-enigma - 2014/04/06 23:30:00

Twitter の方で、この件についてコメントを頂いた。なにやら、generic-function をサブクラス下して MOP でイジってやると思い通りのことができそうなお話。ただ、まだそこまで自分にやれるかどうか‥‥‥。

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

 

このページのタグ

Page tag : Common Lisp

Page tag : STLとその移植

 

 


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