#:g1: defstarの紹介

Posted 2014-03-14 16:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の74日目です。

defstarとはなにか

 defstarは、Paul Sexton氏作の定義マクロ系ユーティリティです。

パッケージ情報

パッケージ名defstar
プロジェクトサイト eeeickythump / defstar — Bitbucket
Quicklisp
Quickdocshttp://quickdocs.org/defstar

インストール方法

(ql:quickload :defstar)

試してみる

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

 大まかに言ってしまうとdefstarは既存の定義構文に型宣言を付加したものが殆どです。
元の構文名に*が付いた命名規則になっているので大体の使い方は想像が付きます。

defun*

 まずは、defun*ですが、引数と返り値の型の指定が可能です。


(defun* sum ((a real) (b real))
  (+ a b))
;===>
(PROGN
 (DECLAIM (FTYPE (FUNCTION (REAL REAL) T) SUM))
 (DEFUN SUM (A B)
   (DECLARE (TYPE REAL B)
            (TYPE REAL A))
   (THE T (+ A B))))

(defun* (sum -> real) ((a real) (b real)) (+ a b)) ;===> (PROGN (DECLAIM (FTYPE (FUNCTION (REAL REAL) REAL) SUM)) (DEFUN SUM (A B) (DECLARE (TYPE REAL B) (TYPE REAL A)) (THE REAL (+ A B))))

(defun* sum ((a real) (b real)) (:returns real) (+ a b))

(defun* (sum -> (real 0)) ((a (real 0)) (b (real 0))) (+ a b))

(deftype natural () '(real 0)) (defun* (sum -> natural) ((a natural) (b natural)) (+ a b))

(defun* (sum -> real ((>= result 0))) ((a real (>= a 0)) (b real (>= b 0))) (+ a b))

(defun* sum ((a real (>= a 0)) (b real (>= b 0))) (:returns real (>= result 0)) (+ a b))

(defun* (sum -> real) ((a real) (b real)) (:pre (>= a 0) (>= b 0)) (:post (>= result 0)) (+ a b))

(sum 1 1) ;=> 2 (sum 1 -1) ;!> A call to SUM violated the precondition: (>= B 0). ;!> A = 1 ;!> B = -1 (defun* (myfloor -> (values integer integer)) ((n real) (d real)) (floor n d))

(myfloor 8 1/8) ;=> 64 ; 0

 型指定するだけから契約プログラミング的な記述まで可能になっています。

defmethod*

defmethodの方は、


(defmethod* (tiltowaito -> real) (((x real plusp) integer)
                                  ((y real plusp) integer))
  (+ x y))

(tiltowaito 1 1) ;=> 2

という感じで、返り値の型を指定できたり、引数の検査に使う関数を指定できたりもします。
若干Filtered Functionsっぽいですが、こちらはMOPではなくマクロでの実現。

defvar*/defparameter*

 defvar系も型が指定できるという拡張です。

(defvar* (*makanito* string) "makanito")

(defparameter* (*dumapic* string) "dumapic")

チェックの方法を選択する

 基本的にCommon Lispでは、declaim/declare等での型宣言に型チェックの機能は期待できず、きちんとするならば、check-typeで検査する必要がありますが、最近の処理系では最適化オプションでsafetyを最大にすれば大抵は、型チェックのコードが入ります。
defstarでは、declaim/declareに期待するか、check-typeを入れるかを選択できるようになっていて、*check-argument-types-explicitly?*で制御することが可能です。
デフォルトはnilでdeclaim/declareで処理系がチェックのコードを入れてくれることを期待。

(let ((*check-argument-types-explicitly?* t))
  (pprint (macroexpand-1  
           '(defun* (sum -> real) ((a real) (b real))
             (+ a b)))))
;>>  
;>>  (PROGN
;>>   (DECLAIM (FTYPE (FUNCTION (REAL REAL) REAL) SUM))
;>>   (DEFUN SUM (A B)
;>>     (DECLARE (TYPE REAL B)
;>>              (TYPE REAL A))
;>>     (CHECK-TYPE B REAL)
;>>     (CHECK-TYPE A REAL)
;>>     (THE REAL (+ A B))))
;=>  <no values>

(let ((*check-argument-types-explicitly?* nil)) (pprint (macroexpand-1 '(defun* (sum -> real) ((a real) (b real)) (+ a b))))) ;>> ;>> (PROGN ;>> (DECLAIM (FTYPE (FUNCTION (REAL REAL) REAL) SUM)) ;>> (DEFUN SUM (A B) ;>> (DECLARE (TYPE REAL B) ;>> (TYPE REAL A)) ;>> (THE REAL (+ A B)))) ;=> <no values>

まとめ

 今回は、defstarを紹介してみました。
こういうユーティリティは趣味が分かれるかと思いますが、割と綺麗にまとまっているユーティリティかなと思いました。

comments powered by Disqus