11.2  deftype, ユーザによる型定義

CLOS によりクラス定義を行うと,それはそのまま Common Lisp の型となりますが,CLOS を使わなくてもユーザが自由に新しいデータの型を定義することができます.たとえば,XMLスキーマのデータタイプをLisp でも使いたいとしましょう.つまり,実質はLisp のデータなんだけれども,それを見かけ上,xsd データタイプとして使いたい,という場合です.そういうときに便利に使えるのがマクロ deftype です.xsd データタイプ定義をまとめてここに置いておきました.必要な方はダウンロードしてみてください.

まずは簡単な場合から,cl:integer をそのまま xsd:integer として定義する場合には次のようになります.

(deftype xsd:integer () 'cl:integer)

ここでは先に xsd パッケージが定義されていて,そのパッケージ中でシンボル integer が移出されているものとしています.コードの書き方はあたかも deftype が defmacro のように書きます.この場合,引数もなし,ただ単に型の置き換えをしているのみです. deftype についてはCLtL2 に比較的詳しく載っていますので,自分がコード化する場合には是非こちらをご覧ください

xsd:nonNegativeInteger は 0 を含む自然数に相当し,非常によく使われます.それはこんな風に定義できます.

(deftype xsd:nonNegativeInteger () '(cl:integer 0 cl:*))

この書き方は,cl:integer のうち,範囲は 0 から cl:* まで,ということなのですが,cl:* はワイルドカード,こういう書き方だと Lisp の型判定で無限大みたいな解釈になります.

先に,型は集合みたいな説明のしかたをしましたが,ある決まったメンバーのみで作られる型を定義するには次のように書きます.

(deftype xsd:boolean () '(cl:member xsd:true xsd:false))

ここで cl:member は関数と思わないで,あくまでも型指定子の一部だと思ってください.型判定の中で解釈されます.

型指定子を使って and,or,not により合成し,新たな型指定子を定義することもできます.たとえば,こんな風.

(deftype xsd:anySimpleType ()
'(cl:or xsd:string xsd:float xsd:double xsd:decimal xsd:anyURI xsd:boolean))

and や or を使った型の型判定では,書かれた順番に条件がテストされて,and 条件が叶わなければ途中で nil,or 条件がかなえば途中で t が返りますので,and の組み合わせではフィルタの役目をする条件を前に置くことが推奨されています.

究極の型指定の仕方があります.ある型を判定するための述語を定義しておいて,それを (satisfies 型述語) というように使います.

(deftype myOwnComplexType () '(cl:satisfies  myOwnComplexType-p))

こうすると (myOwnComplexType-p XObject) と書かなくても (typep XObject 'myOwnComplexType) と組み込みの型と同じように使えるようになります.これは typecase を使う場合には必須になりますね.