Difference between revisions of "型"

From HaskellWiki
Jump to navigation Jump to search
 
Line 70: Line 70:
 
この例ではスートと数字を / から[[String]]やIntに変換できるように[[deriving]]節を使っています。これによって同値性や順序を確認できます。型変数がないような型を使うことで、同値性はどのコンストラクタが使われたかに依存し、順序は記述の順番に依存します。例えば<code>Three</code>は<code>Queen</code>よりも小さいです。
 
この例ではスートと数字を / から[[String]]やIntに変換できるように[[deriving]]節を使っています。これによって同値性や順序を確認できます。型変数がないような型を使うことで、同値性はどのコンストラクタが使われたかに依存し、順序は記述の順番に依存します。例えば<code>Three</code>は<code>Queen</code>よりも小さいです。
   
Now we define an actual <code>Card</code>
+
では実際に<code>Card</code>を定義してみましょう。
 
<haskell>
 
<haskell>
 
data Card = Card {value::CardValue,
 
data Card = Card {value::CardValue,
Line 76: Line 76:
 
deriving (Read, Show, Eq)
 
deriving (Read, Show, Eq)
 
</haskell>
 
</haskell>
  +
この定義では[[field]]を使っています。これによって<code>Card</code>の2箇所に対してアクセスする既成の関数が手に入ります。また、もう一度いいますが、型変数([[type variables]])が使われていませんが、データの[[constructor]]は2つの引数に対して特定の型、つまり<code>CardValue</code>と<code>Suit</code>であることを要求しています。
In this definition, we use [[field]]s, which give us ready made functions to
 
access the two parts of a <code>Card</code>. Again, [[type variables]] were not
 
used, but the data [[constructor]] requires its two parameters to be of
 
specific types, <code>CardValue</code> and <code>Suit</code>.
 
   
The deriving clause here only specifies three of our desired [[Type class|Class]]es, we supply [[instance]] declarations for [[Ord]] and [[Enum]].
+
ここでのderiving節は単に求められている3つの[[Type class|Class]]を明記しているだけです。[[instance]]宣言を[[Ord]][[Enum]]に対して行います。
 
<haskell>
 
<haskell>
 
instance Ord Card where
 
instance Ord Card where
Line 91: Line 88:
 
fromEnum c = 4*(fromEnum (value c)) + (fromEnum (suit c))
 
fromEnum c = 4*(fromEnum (value c)) + (fromEnum (suit c))
 
</haskell>
 
</haskell>
  +
最後に、<code>Deck</code>型を<code>Card</code>のリストに対するエイリアスとして宣言し、デッキをリスト内包([[list comprehension]])として表します。
Finally, we alias the type <code>Deck</code> to a list of <code>Card</code>s
 
and populate the deck with a [[list comprehension]]
 
 
<haskell>
 
<haskell>
 
type Deck = [Card]
 
type Deck = [Card]
Line 102: Line 98:
 
==追加してください==
 
==追加してください==
   
  +
より図解が多い例を歓迎します。
Further illustrative examples would be most appreciated.
 
   
 
==参考==
 
==参考==
  +
データ[[constructor]]や[[Type class|class]]に関する(必要な)記事を読んでみてください。また[http://haskell.org/definition/haskell98-report.pdf Haskell 98 report]やご自分が選んだ実装([[GHC/Documentation]]など)にも最新の情報があるかもしれません。R
Read the (wanted) articles about data [[constructor]]s and [[Type class|class]]es. As well the
 
[http://haskell.org/definition/haskell98-report.pdf Haskell 98 report] and
 
your chosen implementation (e.g. [[GHC/Documentation]]) have the latest words.
 
   
 
*[[Determining the type of an expression]] - Let the Haskell compiler compute the type of expression
 
*[[Determining the type of an expression]] - Let the Haskell compiler compute the type of expression

Latest revision as of 14:11, 7 December 2009

Haskellでは とはプログラム内で用いるデータを表現するものです。

データの宣言

Haskellでは型をdata宣言を通じて導入、または宣言します。一般的にデータ宣言はこのように行います。

data [context =>] type tv1 ... tvi = con1  c1t1 c1c2... c1tn |
                      ... | conm cmt1 ... cmtq
                    [deriving]

まだHaskellをあまり理解していいない方にはおそらく説明になっていないかもしれません。

上の宣言の本質は、dataキーワードを使って、付属的なコンテキストを与え、型の名前と多くのtype variableを与えることにあります。 その後、多くのconstructorが続きます。これらはtype variabletype constantのリストになっています。最後に付属的にderivingが続きます。

他にも多くの微妙な事柄が必要です。たとえばデータコンストラクタに必要なパラメータはeagerでなければならない、どんなclassderivingの中で許可されているか、コンストラクタ内のfield名の使い方、contextが実際になにをするのかなどです。これらについてはそれぞれの記事を参照してください。

いくつかの例を見てみましょう。Haskellの標準データ型Maybeは普通このように宣言されています。

 data Maybe a = Just a | Nothing

これは、Maybe型はaで表される1つの型変数を持っていて、JustNothingという2つのconstructorを持っているということを意味しています。(Haskellでは型名とコンストラクタ名は大文字で始まらなければいけないことに注意してください) Justコンストラクタは1つのパラメータ"a"をとります。

他の例として、二分木 (binary Tree)を考えてみましょう。このように表されます。

 data Tree a = Branch (Tree a) (Tree a) | Leaf a

ここで、Treeのコンストラクタの1つBranchはコンストラクタのパラメータには2つのtreeを取る一方で、Leafは型変数"a"だけを取ります。Haskellではこういった再帰型は非常によく使われるpatternsです。

型と新しい型

他のHaskellプログラムに型を導入する方法はtypenewtypeを用いて行う方法です。

typeはあるデータコンストラクタに対してシノニムを与えます。newtypeではある型をリネームして新しいコンストラクタを作成する必要があります。

type宣言をするときは、型シノニムとその基底型はほぼいつでも交換可能です。(instance宣言を扱う場合にいくつか制限があります)たとえば、このような宣言があったとします。

 type Name = String

Stringをシグネチャにもつどのような関数Name型の要素に対して用いることができます。

しかしながら、もしこのような宣言の場合:

 
  newtype FirstName = FirstName String

先ほどの例のようにはいきません。関数の宣言においては実際にFirstNameに対して定義されてなければいけません。このような要求を軽減するために、しばしばデコンストラクタを宣言します。こんな具合です:

  unFirstName :: FirstName -> String
  unFirstName (FirstName s) = s

これはよくnewtype内のfieldを使うときに行われます。(フィールドを幅広く使う人もいる一方で、多くの人がHaskellのフィールド実装は甘いと考えています。Programming guidelinesFuture of Haskellも参照してください)

簡単な例

たとえばトランプのゲームのブリッジを実装することを考えてみましょう。まずカードを表す何かが必要です。一つの例としてはこんなかんじです。

まず、スートと数を表すデータ型を作成します。

 data Suit = Club | Diamond | Heart | Spade
     deriving (Read, Show, Enum, Eq, Ord)

 data CardValue = Two | Three | Four
     | Five | Six | Seven | Eight | Nine | Ten 
     | Jack | Queen | King | Ace
    deriving (Read,  Show, Enum, Eq, Ord)

この例ではスートと数字を / からStringやIntに変換できるようにderiving節を使っています。これによって同値性や順序を確認できます。型変数がないような型を使うことで、同値性はどのコンストラクタが使われたかに依存し、順序は記述の順番に依存します。例えばThreeQueenよりも小さいです。

では実際にCardを定義してみましょう。

 
 data Card = Card {value::CardValue, 
                    suit::Suit}
    deriving (Read, Show, Eq)

この定義ではfieldを使っています。これによってCardの2箇所に対してアクセスする既成の関数が手に入ります。また、もう一度いいますが、型変数(type variables)が使われていませんが、データのconstructorは2つの引数に対して特定の型、つまりCardValueSuitであることを要求しています。

ここでのderiving節は単に求められている3つのClassを明記しているだけです。instance宣言をOrdEnumに対して行います。

 instance Ord Card where
      compare c1 c2  | (value c1 == (value c2)) = compare (suit c1) (suit c2)
                     | otherwise = compare (value c1) (value c2)

 instance Enum Card where
      toEnum n = Card (toEnum (n `div` 4)) (toEnum (n `mod` 4))
      fromEnum c =  4*(fromEnum (value c)) + (fromEnum (suit c))

最後に、Deck型をCardのリストに対するエイリアスとして宣言し、デッキをリスト内包(list comprehension)として表します。

 type Deck = [Card]

 deck::Deck
 deck = [Card val su | val <- [Two .. Ace], su <- [Club .. Spade]]

追加してください

より図解が多い例を歓迎します。

参考

データconstructorclassに関する(必要な)記事を読んでみてください。またHaskell 98 reportやご自分が選んだ実装(GHC/Documentationなど)にも最新の情報があるかもしれません。R

Languages: en