今から少しだけ論理の話をします.型判定するアルゴリズムについて説明するには,どうしても論理の用語を使うことになるからです.論理と言っても型判定の範囲では変数はでてきませんから,表面上は命題論理になります.命題論理では,and,or,not,=> などを使った論理表現ではない素論理式をアトム(atom) と言います(今関数は考えないことにする).Lisp では通常これを Cat とか Mother のようなシンボルで表現します.Erlang や Prolog では論理式でアトムになれるのはシンボルだけですが,ここでは以後論理式アトムに任意のオブジェクトが来てもいいことにします.CLOS オブジェクトでもいいことにする.アトムとその否定 (not アトム) は特別にリテラルと言います.今からやりたいことは,and,or,not を含む一般の型指定子(とりあえずsatisfies はないことにしておきましょう)を命題論理式のように見て,新規にユーザが型定義をした場合,それを既存の型階層の中に subtypep の意味で正しく位置づけたい,ということです.これを述語論理の方では classify するとか言います.一般の型指定子を既存体系の中に classify すると言っても,アトムのレベルになると,どちらが上位か下位か(それとも関係ないか)は,何かほかに情報がないと判定できません.幸いなことに,Common Lisp では型とCLOSのクラスが統合されており,しかも既存のCommon Lisp の型に丁度対応するCLOSクラスが定義されています(すべてではない).CLtL2 の表28-1には既存の型におけるクラス優先順位リストが掲げてあります.そして,型名からCLOS クラスオブジェクトを得たり,表28-1にあるような情報を得るのは次のように簡単なことです.

cg-user(1): (find-class 't)
#<built-in-class t>
cg-user(2): (find-class 'integer)
#<built-in-class integer>
cg-user(3): (mop:class-precedence-list (find-class 'integer))
(#<built-in-class integer> #<built-in-class rational> #<built-in-class real>
#<built-in-class number> #<built-in-class t>)
cg-user(4): (mop:class-precedence-list (find-class 'string))
(#<built-in-class string> #<built-in-class vector> #<built-in-class array>
#<built-in-class sequence> #<built-in-class t>)

これ以降,我々も何か新しくアトミックな型定義するときは,このようなCLOSのクラス定義をして,そのsuperclass として上位クラスを定義してやることにしましょう.たとえば,Cat の上位に Mamal を置き,Mammal の上位に Animal を置く,というようなことです.ただし,XML データスキーマについては値空間をCommon Lisp に写像するために,deftype でCommon Lisp にマッピングします.たとえばこんな具合です.

cg-user(5): (defpackage :xsd
  (:nicknames :xs)
  (:use ) ; supressing using common lisp package
  (:export "string" "boolean" "decimal" "float" "double" "dataTime" "time" "date"
           "gYearMonth" "gYear" "gMonthDay" "gDay" "gMonth" "hexBinary" "base64Binary"
           "anyURI" "normallizedString" "token" "language" "NMTOKEN" "Name" "NCName"
           "integer" "nonPositiveInteger" "negativeInteger" "long" "int" "short" "byte"
           "nonNegativeInteger" "unsignedLong" "unsignedInt" "unsignedShort" "unsignedByte"
           "positiveInteger" "simpleType" "anySimpleType" "true" "false"
           "duration" "duration-year" "duration-month" "duration-day" "duration-hour"
           "duration-minute" "duration-second")
  (:documentation "http://www.w3.org/2001/XMLSchema#"))
#<The xsd package>
cg-user(6): (deftype xsd:integer () 'integer)
xsd:integer
cg-user(7): (deftype xsd:string () 'string)
xsd:string

XML データスキーマの型階層についてはここにあります.このような型階層があったとき,重要な情報は上位下位関係のみならず,二つの兄弟の型が互いに疎(disjoint)であるかどうか,また,上位型に対して複数の下位型が網羅的かどうかです.たとえば,xsd:decimal に対して xsd:integer は下位型ですが,網羅的ではありません.また,xsd:long は xsd:nonNegativeInteger や xsd:nonPositiveInteger とdisjoint ではありません.xsd:nonNegativeInteger と xsd:nonPositiveInteger は 0 という共通要素を持ちますが,(or xsd:nonNegativeInteger xsd:nonPositiveInteger) を型とするとこれは xsd:integer と同等です.ちょっと調べてみると,Allegro Common Lisp でも SBCL でも,satisfies は別としてそれ以外については,きちんと計算してくれるようです.

cg-user(9): (cl:deftype xsd:positiveInteger () '(cl:integer 1 cl:*))
xsd:positiveInteger
cg-user(10): (cl:deftype xsd:nonPositiveInteger () '(cl:integer cl:* 0))
xsd:nonPositiveInteger
cg-user(11): (cl:deftype xsd:negativeInteger () '(cl:integer cl:* -1))
xsd:negativeInteger
cg-user(12): (cl:deftype xsd:nonNegativeInteger () '(cl:integer 0 cl:*))
xsd:nonNegativeInteger
cg-user(13): (cl:deftype xsd:int () '(cl:signed-byte 32))
xsd:int
cg-user(14): (subtypep 'xsd:positiveInteger 'xsd:integer)
t
t
cg-user(15): (subtypep 'xsd:positiveInteger 'cl:integer)
t
t
cg-user(16): (subtypep 'cl:integer 'xsd:integer)
t
t
cg-user(17): (subtypep 'xsd:integer 'cl:integer)
t
t
cg-user(18): (subtypep 'xsd:positiveInteger 'xsd:nonNegativeInteger)
t
t
cg-user(19): (subtypep 'xsd:nonNegativeInteger 'xsd:positiveInteger)
nil
t
cg-user(20): (subtypep '(or xsd:nonNegativeInteger xsd:nonPositiveInteger)
'xsd:integer)
t
t
cg-user(21): (subtypep 'xsd:integer
'(or xsd:nonNegativeInteger xsd:nonPositiveInteger))
t
t
cg-user(22): (subtypep '(or xsd:nonNegativeInteger xsd:nonPositiveInteger xsd:int)
'xsd:integer)
t
t
cg-user(23): (subtypep 'xsd:integer
'(or xsd:nonNegativeInteger xsd:nonPositiveInteger xsd:int))
t
t

といいつつも,XMLデータスキーマの値空間からLispデータの値空間へのマッピングは,いろいろと根回しが必要なことがあって,いましばらく後回しにして,CLOSの型階層についてのみ考えましょう.(まだまだ続く)