#:g1: cl-opの紹介

Posted 2014-01-06 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の7日目です。

cl-opとはなにか

GOOというArcとDylanに影響を受けたLisp方言がありますが、GOOのopというlamdaの短縮形をCommon Lispに移植したものです。

パッケージ情報

パッケージ名cl-op
Quicklisp
公式サイト cl-op -

Partial application library for Common Lisp - Google Project Hosting

CLiKihttp://cliki.net/cl-op
Quickdocshttp://quickdocs.org/cl-op

インストール方法

(ql:quickload :cl-op)
※そのまま読み込むと標準のリードテーブルを変更してしまいます。

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 使い方は単純で、Arcや、Clojureのfn的なものです。

(mapcar (cl-op:op list 1 _ 2) 
        '(a b c))
;=>  ((1 A 2) (1 B 2) (1 C 2))

他に、関数型言語でお馴染のcomposeや、flipもあります。

cl-opのリーダーマクロについて

 ちょっとコードを眺めてみましたが、どうも#'を改造してしまうようです。
これは、上記のような (cl-op:op list 1 _ 2)を

(mapcar #'(list 1 _ 2)
        '(:a :b :c))
;=>  ((1 :A 2) (1 :B 2) (1 :C 2))

と書けるようにするためのもののようですが、標準のリードテーブルはあまり変更したくないですよね。
こういうパッケージに備えて自分は、ql:quickloadのラッパーを作って利用しています。
*package*をcl-userにしているのは、cl-userから読み込まないと上手くロードできないパッケージが割と存在するため。

(defun qload (item)
  (let ((cl:*package* (cl:find-package :cl-user))
        (cl:*readtable* (cl:copy-readtable nil)))
    (ql:quickload item)))

(qload :cl-op)

そして別途リーダーマクロを利用したい場合は、別途定義したリードテーブルを利用

(named-readtables:defreadtable :op
  (:merge :standard)
  (:dispatch-macro-char 
   #\# #\' 
   (let ((original-reader (get-dispatch-macro-character
                           #\# #\' 
                           (named-readtables:find-readtable :standard))))
     (lambda (stream subchar arg)
       (if (eq (peek-char nil stream nil (values) t) #\()
           (cons 'cl-op:op (read stream nil (values) t))
           (funcall original-reader stream subchar arg))))))

(named-readtables:in-readtable :op)

ちなみにcl-opのコードを読む限りでは、#'(setf foo)形式のことを考えてないみたいです。

Lisp2の悩ましいところ

それはさておき、利用上の注意点ですが、opで#'を書かずに済ませることを優先したため、関数との組み合わせでの使い勝手があまり良くないようです。

(funcall (cl-op:op (cl-op.hof:flip #'coerce) 'list _) "foo")
;!!! error

(funcall (cl-op:op funcall (cl-op.hof:flip #'coerce) 'list _) "foo") ;=> (#f #o #o)

この手のものを作成する上でLisp2は悩ましいですね。

マクロにコンパイラマクロ

 それとOPのマクロに最適化の為にコンパイラマクロが付いていました。
マクロにもコンパイラマクロが付けられるとは勉強になりました。

(defmacro foo () :macro)

(define-compiler-macro foo (&whole whole &rest args) (declare (ignore args)) (case (car whole) (cl:funcall :compmacro) (otherwise whole)))

(foo) ;=> :MACRO (eval-when (:compile-toplevel) (defun bar () (funcall #'foo)))

(bar) ;=> :COMPMACRO

cl-opではβ-簡約をするのに利用しています。

cl-opの紹介/関連記事等

まとめ

 今回は、cl-opを紹介してみました。
どうもあまり長所を紹介してない気がしますが、使い方によっては便利だと思います。

comments powered by Disqus