The Haskell 98 Report
top | back | next | contents | function index


10  導出インスタンスの特化

導出インスタンスdata あるいは newtype 宣言に付随して、自動的に生成されたインスタンス宣言である。導出インス タンス宣言の本体は、対応する型の定義より構文的に導出される。導出イン スタンスはコンパイラが知っているクラスに関してのみ可能である。これら のクラスはプレリュードおよび標準ライブラリで定義されているものである。 この章では、プレリュードによって定義されているクラスの導出について解 説する。

もし T が次のように定義された代数的データ型であるなら

data cx => T u1 ... uk = K1 t11 ... t1k1 | ...| Kn tn1 ... tnkn
deriving (C1, ..., Cm)

(ここで m>=0 および括弧は m=1 の場合省略される)、 導出インスタンス宣言は、クラス C について、以下の条件が なりたてば、可能となる。

  1. CEqOrdEnumBoundedShow あるいは Read のうちの どれか。

  2. cx' =>C tij が構成要素の型 tij のそれぞれについて保存されているような 文脈 cx' がある。

  3. CBounded である場合。このとき、この型は列挙型 (すべての構成子が無引数)であるか、または構成子が一つしかないかの どちらかでなければならない。

  4. CEnum である場合。このとき、この型は列挙型 でなければならない。

  5. T u1 ... ukC のインスタンスとしたプログラム内の別の個所で明示的な インスタンス宣言があってはならない。

導出インスタンスという目的のために、newtype 宣言は 単一構成子の data 宣言として扱われる。

もし、deriving 形式があれば、インスタンス宣言は自動的に 各 Ci 上で T u1 ... uk に対して生成される。もし、Ci のどれかに 対して、導出インスタンス宣言が不可能であれば、静的エラーとなる。 もし、導出インスタンスが必要とされていないのなら、 deriving 形式は省略するるか、あるいは、 deriving () を使うことができる。

各導出インスタンス宣言は次の形式をとる。

instance (cxcx') => Ci (T u1 ... uk) where { d }

ここで、dCi および T のデータ型宣言に依存して(あとの節で解説するように)自動的に導出される。

文脈 cx' は上記(2)を満す最小文脈である。相互再帰的なデータ型に ついてはコンパイラはこれを求めるためにある種の不動点計算が必要となる であろう。

導出可能な各プレリュードのクラスに対する導出インスタンスののこりの詳 細についてはここで与える。これらの変換で使われている自由変数と構成子 はつねに Prelude で定義されたエンティティを参照している。

10.1  Eq および Ord の導出インスタンス

Eq および Ord の導出インスタンスによって自動的に導 入されるクラスメソッドは (==)(/=)compare(<)(<=)(>)(>=)max および min である。後の 7 つ の演算子はその引数を与えられた構成子集合の字句順に比較するために定義 されている。データ型宣言の中で先にでてくる構成子があとにでてくる構築 子よりも小さいと看倣される。たとえば、Bool 型では (True > False) == True である。

導出された比較は常に構成子を左から右へトラバースする。以下は この性質を説明している。

  (1,undefined) == (2,undefined) 
=>    False
  (undefined,1) == (undefined,2) 
=>    _|_

Eq および Ord クラスの導出された演算はすべて二つの 引数に対して正格である。たとえば、 False < _|__|_ である。 これは、FalseBool 型の最初の構成子であるにも かかわらず、そうである。

10.2  Enum の導出インスタンス

Enum クラスに対する導出インスタンス宣言は列挙型(データ型が 無引数構成子のみを含む型)にのみ可能である。

無引数構成子は左から右へ 0 から n-1 の数字が振られているものと仮定す る。succ および pred 演算子はこの付番図式のもとで、 それぞれ、次の値、前の値を与える。最大の要素への succ の適用 あるいは最小の要素への pred の適用はエラーである。

toEnum および fromEnum 演算子は列挙された値と Int 間の写像である。toEnumInt の 引数が構成子のインデックスの一つでなければ、実行時エラーを起す。

のこりのメソッドの定義は、

  enumFrom x           = enumFromTo x lastCon
  enumFromThen x y     = enumFromThenTo x y bound
                       where
                         bound | fromEnum y >= fromEnum x = lastCon
                               | otherwise                = firstCon
  enumFromTo x y       = map toEnum [fromEnum x .. fromEnum y]
  enumFromThenTo x y z = map toEnum [fromEnum x, fromEnum y .. fromEnum z]

である。ここで、firstConlastCon はそれぞれ、 data 宣言中にあげられた構成子の最初のものと最後のものである。 例えば次のようなデータ型を考えると

  data  Color = Red | Orange | Yellow | Green  deriving (Enum)

こんなふうになる。

  [Orange ..]         ==  [Orange, Yellow, Green]
  fromEnum Yellow     ==  2

10.3  Bounded の導出インスタンス

Bounded クラスは、この型の最小要素および最大要素を定義する minBound および maxBound というクラスメソッドを 導入する。列挙については、data 宣言中にリストアップされた 最初および最後の構成子が、境界となる。単一構成子の型については、 その構成子が構成要素の境界として適用される。たとえば、次のデータ型

  data  Pair a b = Pair a b deriving Bounded

は、以下のような Bounded のインスタンスを生成する。

  instance (Bounded a,Bounded b) => Bounded (Pair a b) where
    minBound = Pair minBound minBound
    maxBound = Pair maxBound maxBound

10.4  Read および Show の導出インスタンス

Read および Show の導出インスタンスによって自動的に 導入されるクラスメソッドは showsPrecreadsPrecshowList および readList である。これらは、 値から文字列へ変換と、パーズ文字列から値への変換のために用いられる。

関数 showsPrec d x r は優先レベル d (0 から 11 の数)、値 x および文字列 r を受けいれ、r に連接された x の表現を返す。 showsPrec 以下の規約を見たす。

showsPrec d x r ++ s  ==  showsPrec d x (r ++ s)

x のトップレベル構築演算子の優先順位が d よりも 小さい場合、表現は括弧で囲まれる。したがって、もし、d0なら結果は括弧にくくられことはなく、d11 でアトミックな式(関数適用の優先度は 10 であることを 思いだすこと)でなければ、常に括弧にくくられる。追加のパラメータ r は、もしツリーのような構造が、その木のサイズの二乗の オーダではなく線形のオーダでプリントする場合には不可欠である。

関数 readsPrec d s は優先レベル d (0 から 10 の値) および文字列 s を受けいれ、 その文字列を先頭からパーズして、対(パーズされた値と残りの文字列)の リストを返す。もし、成功したパーズがなれば、空のリストを返す。括弧の ない中置演算子適用をパーズすると、その演算子の優先度が d より大きいか等しい場合にかぎって成功する。

この場合以下のようになっているはずである。

(x,"")(readsPrec d (showsPrec d x "")) の要素である。

すなわち、readsPrecshowsPrec によって生成された 文字列をパーズできなければならない。また、showsPrec へ与えた 最初の値を生成できなければならない。

showList および readList はオブジェクトのリストを非 標準の表記法で表現することを可能にする。これは特に文字列 (Char のリスト)の場合に有用である。

readsPrec は文字列から標準の型の正しい表現をパーズする。 これは、引用部で囲まれた文字列のみ受理される。その他のリストについて は、四角括弧形式 [...] のみ受けつける。完全な 詳細については 8 章を 参照のこと。

show の結果は、構文的として正しい式で、その型が宣言されたと ころで有効な配置宣言によって与えられた定数のみを含む。また、 データ型のなかで定義された構成子名、括弧、および空白のみを含む。 ラベル付構成子フィールドが用いられた場合には、ブレース、コンマ フィールド名、イコール記号も用いられる。括弧は必要とされる個所に のみ追加され、結合性は無視する。改行が追加されることはない。 show の結果は、すべての構成要素が読み込み可能であれば、 read で読み込みが可能である。(このことは、プレリュードで 定義されているすべてのインスタンスについて真であるが、ユーザ定義の インスタンスについては、真ではないこともありうる。)

Read の導出インスタンスは以下の仮定をし、Show の 導出インスタンスがこれに従う。

導出された Read および Show インスタンスは いくつかの場合には適切ではないことがある。問題は、

10.5  

完全な例として木のデータ構造を考えよ。

  data Tree a = Leaf a | Tree a :^: Tree a
       deriving (Eq, Ord, Read, Show)

Bounded および Enum に対する自動のインスタンス 宣言の導出は不可能である。それは Tree は列挙型でもなければ 単一構成子のデータ型でもないからである。Tree に対する完全な インスタンス宣言は図 10.1 である。 デフォルトのクラスメソッド定義を暗黙のうちに使っていることに注意せよ。 たとえば、<= のみが Ord に対して定義されており、 その他のクラスメソッド(<>>=max および min) は 図 6.1 に示したクラス 宣言中で与えられたデフォルトによって定義されている。


infixr 5 :^:
data Tree a =  Leaf a  |  Tree a :^: Tree a

instance (Eq a) => Eq (Tree a) where
        Leaf m == Leaf n  =  m==n
        u:^:v  == x:^:y   =  u==x && v==y
             _ == _       =  False

instance (Ord a) => Ord (Tree a) where
        Leaf m <= Leaf n  =  m<=n
        Leaf m <= x:^:y   =  True
        u:^:v  <= Leaf n  =  False
        u:^:v  <= x:^:y   =  u<x || u==x && v<=y

instance (Show a) => Show (Tree a) where

        showsPrec d (Leaf m) = showParen (d > app_prec) showStr
          where
             showStr = showString "Leaf " . showsPrec (app_prec+1) m

        showsPrec d (u :^: v) = showParen (d > up_prec) showStr
          where
             showStr = showsPrec (up_prec+1) u . 
                       showString " :^: "      .
                       showsPrec (up_prec+1) v
                -- Note: right-associativity of :^: ignored

instance (Read a) => Read (Tree a) where

        readsPrec d r =  readParen (d > up_prec)
                         (\r -> [(u:^:v,w) |
                                 (u,s) <- readsPrec (up_prec+1) r,
                                 (":^:",t) <- lex s,
                                 (v,w) <- readsPrec (up_prec+1) t]) r

                      ++ readParen (d > app_prec)
                         (\r -> [(Leaf m,t) |
                                 ("Leaf",s) <- lex r,
                                 (m,t) <- readsPrec (app_prec+1) s]) r

up_prec  = 5    -- Precedence of :^:
app_prec = 10   -- Application has precedence one more than
-- the most tightly-binding operator

Figure 10.1

導出インスタンスの例


The Haskell 98 Report
top | back | next | contents | function index
December 2002