Difference between revisions of "型"

From HaskellWiki
Jump to: navigation, search
 
(5 intermediate revisions by the same user not shown)
Line 1: Line 1:
In Haskell, '''types''' are how you describe the data your program will work with.
 
  +
Haskellでは '''型''' とはプログラム内で用いるデータを表現するものです。
   
 
[[Category:Language]]
 
[[Category:Language]]
Line 5: Line 5:
 
==データの宣言==
 
==データの宣言==
   
One introduces, or declares, a type in Haskell via the <code>data</code> statement. In general a data declaration looks like:
 
  +
Haskellでは型を<code>data</code>宣言を通じて導入、または宣言します。一般的にデータ宣言はこのように行います。
   
 
data [context =>] type tv1 ... tvi = con1 c1t1 c1c2... c1tn |
 
data [context =>] type tv1 ... tvi = con1 c1t1 c1c2... c1tn |
Line 11: Line 11:
 
[deriving]
 
[deriving]
   
which probably explains nothing if you don't already know Haskell!
 
  +
まだHaskellをあまり理解していいない方にはおそらく説明になっていないかもしれません。
   
The essence of the above statement is that you use the keyword <hask>data</hask>,
 
  +
上の宣言の本質は、<hask>data</hask>キーワードを使って、付属的なコンテキストを与え、型の名前と多くの[[type variable]]を与えることにあります。
supply an optional context, give the type name and a variable number of
 
  +
その後、多くの[[constructor]]が続きます。これらは[[type variable]]か[[type constant]]のリストになっています。最後に付属的に<code>deriving</code>が続きます。
[[type variable]]s. This is then followed by a variable number of [[constructor]]s, each of which has a list of [[type variable]]s or [[type constant]]s. At the end, there is an optional <code>deriving</code>.
 
   
There are a number of other subtleties associated with this, such as requiring
 
  +
他にも多くの微妙な事柄が必要です。たとえばデータコンストラクタに必要なパラメータは[[eager]]でなければならない、どんな[[Type class|class]]が[[deriving]]の中で許可されているか、コンストラクタ内の[[field]]名の使い方、[[context]]が実際になにをするのかなどです。これらについてはそれぞれの記事を参照してください。
parameters to the data constructors to be [[eager]], what [[Type class|class]]es are
 
allowed in the [[deriving]], use of [[field]] names in the constructors
 
and what the [[context]] actually does. Please refer to the specific articles for more on each of those.
 
   
Let's look at some examples. The Haskell standard data type [[Maybe]] is typically declared as:
 
  +
いくつかの例を見てみましょう。Haskellの標準データ型[[Maybe]]は普通このように宣言されています。
 
<haskell>
 
<haskell>
 
data Maybe a = Just a | Nothing
 
data Maybe a = Just a | Nothing
 
</haskell>
 
</haskell>
What this means is that the type '''Maybe''' has one type variable, represented by the ''a'' and two [[constructor]]s '''Just''' and '''Nothing'''. (Note that Haskell requires type names and constructor names to begin with an uppercase letter). The '''Just''' constructor takes one parameter, ''a''.
 
  +
これは、'''Maybe'''型は''a''で表される1つの型変数を持っていて、'''Just'''と'''Nothing'''という2つの[[constructor]]を持っているということを意味しています。(Haskellでは型名とコンストラクタ名は大文字で始まらなければいけないことに注意してください)
  +
'''Just'''コンストラクタは1つのパラメータ"a"をとります。
   
As another example, consider binary [[Tree]]s. They could be represented by:
 
  +
他の例として、二分木 (binary [[Tree]])を考えてみましょう。このように表されます。
 
<haskell>
 
<haskell>
 
data Tree a = Branch (Tree a) (Tree a) | Leaf a
 
data Tree a = Branch (Tree a) (Tree a) | Leaf a
 
</haskell>
 
</haskell>
Here, one of the constructors, '''Branch''' of '''Tree''' takes two trees as
 
  +
ここで、'''Tree'''のコンストラクタの1つ'''Branch'''はコンストラクタのパラメータには2つのtreeを取る一方で、'''Leaf'''は型変数"a"だけを取ります。Haskellではこういった再帰型は非常によく使われる[[:Category:Idioms |patterns]]です。
parameters to the constructor, while '''Leaf''' takes the type variable ''a''. This type of recursion is a very common [[:Category:Idioms |pattern]] in Haskell.
 
   
 
==型と新しい型==
 
==型と新しい型==
   
The other two ways one may introduce types to Haskell programs are via the
 
  +
他のHaskellプログラムに型を導入する方法は<hask>type</hask>と<hask>newtype</hask>を用いて行う方法です。
<hask>type</hask> and <hask>newtype</hask> statements.
 
   
<hask>type</hask> introduces a synonym for a type and uses the same data
 
  +
<hask>type</hask>はあるデータコンストラクタに対してシノニムを与えます。<hask>newtype</hask>ではある型をリネームして新しいコンストラクタを作成する必要があります。
constructors. <hask>newtype</hask> introduces a renaming of a type and
 
requires you to provide new constructors.
 
   
When using a <hask>type</hask> declaration, the type synonym and its base type
 
  +
<hask>type</hask>宣言をするときは、型シノニムとその基底型はほぼいつでも交換可能です。([[instance]]宣言を扱う場合にいくつか制限があります)たとえば、このような宣言があったとします。
are interchangeble almost everywhere (There are some restrictions when dealing with [[instance]] declarations). For example, if you had the declaration:
 
 
<haskell>
 
<haskell>
 
type Name = String
 
type Name = String
 
</haskell>
 
</haskell>
then any [[function]] you had declared that had <hask>String</hask> in its
 
  +
<hask>String</hask>をシグネチャにもつどのような[[関数]]も<code>Name</code>型の要素に対して用いることができます。
signature could be used on any element of type <code>Name</code>
 
   
However, if one had the declaration:
 
  +
しかしながら、もしこのような宣言の場合:
 
<haskell>
 
<haskell>
 
newtype FirstName = FirstName String
 
newtype FirstName = FirstName String
 
</haskell>
 
</haskell>
this would no longer be the case. Functions would have to be declared that
 
  +
先ほどの例のようにはいきません。関数の宣言においては実際に'''FirstName'''に対して定義されてなければいけません。このような要求を軽減するために、しばしばデコンストラクタを宣言します。こんな具合です:
actually were defined on '''FirstName'''. Often, one creates a deconstructor
 
at the same time which helps alleviate this requirement. e.g.:
 
 
<haskell>
 
<haskell>
 
unFirstName :: FirstName -> String
 
unFirstName :: FirstName -> String
 
unFirstName (FirstName s) = s
 
unFirstName (FirstName s) = s
 
</haskell>
 
</haskell>
This is often done by the use of [[field]]s in the <code>newtype</code>. (Note
 
  +
これはよく<code>newtype</code>内の[[field]]を使うときに行われます。(フィールドを幅広く使う人もいる一方で、多くの人がHaskellのフィールド実装は甘いと考えています。[[Programming guidelines]]や[[Future of Haskell]]も参照してください)
that many consider the Haskell field implementation sub-optimal, while
 
others use it extensively. See [[Programming guidelines]] and [[Future of Haskell]])
 
   
 
==簡単な例==
 
==簡単な例==
   
Suppose you want to create a program to play bridge. You need something to represent cards. Here is one way to do that.
 
  +
たとえばトランプのゲームのブリッジを実装することを考えてみましょう。まずカードを表す何かが必要です。一つの例としてはこんなかんじです。
   
First, create data types for the suit and card number.
 
  +
まず、スートと数を表すデータ型を作成します。
 
<haskell>
 
<haskell>
 
data Suit = Club | Diamond | Heart | Spade
 
data Suit = Club | Diamond | Heart | Spade
Line 81: Line 68:
 
deriving (Read, Show, Enum, Eq, Ord)
 
deriving (Read, Show, Enum, Eq, Ord)
 
</haskell>
 
</haskell>
Each of these uses a [[deriving]] clause to allow us to convert them from / to [[String]] and Int, test them for equality and ordering. With types like this,
 
  +
この例ではスートと数字を / から[[String]]やIntに変換できるように[[deriving]]節を使っています。これによって同値性や順序を確認できます。型変数がないような型を使うことで、同値性はどのコンストラクタが使われたかに依存し、順序は記述の順番に依存します。例えば<code>Three</code>は<code>Queen</code>よりも小さいです。
where there are no [[type variable]]s, equality is based upon which constructor is used and order by the order you wrote them. e.g. <code>Three</code> is less than <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 90: Line 76:
 
deriving (Read, Show, Eq)
 
deriving (Read, Show, Eq)
 
</haskell>
 
</haskell>
In this definition, we use [[field]]s, which give us ready made functions to
 
  +
この定義では[[field]]を使っています。これによって<code>Card</code>の2箇所に対してアクセスする既成の関数が手に入ります。また、もう一度いいますが、型変数([[type variables]])が使われていませんが、データの[[constructor]]は2つの引数に対して特定の型、つまり<code>CardValue</code>と<code>Suit</code>であることを要求しています。
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 105: Line 88:
 
fromEnum c = 4*(fromEnum (value c)) + (fromEnum (suit c))
 
fromEnum c = 4*(fromEnum (value c)) + (fromEnum (suit c))
 
</haskell>
 
</haskell>
Finally, we alias the type <code>Deck</code> to a list of <code>Card</code>s
 
  +
最後に、<code>Deck</code>型を<code>Card</code>のリストに対するエイリアスとして宣言し、デッキをリスト内包([[list comprehension]])として表します。
and populate the deck with a [[list comprehension]]
 
 
<haskell>
 
<haskell>
 
type Deck = [Card]
 
type Deck = [Card]
Line 116: Line 98:
 
==追加してください==
 
==追加してください==
   
Further illustrative examples would be most appreciated.
 
  +
より図解が多い例を歓迎します。
   
 
==参考==
 
==参考==
Read the (wanted) articles about data [[constructor]]s and [[Type class|class]]es. As well the
 
  +
データ[[constructor]]や[[Type class|class]]に関する(必要な)記事を読んでみてください。また[http://haskell.org/definition/haskell98-report.pdf Haskell 98 report]やご自分が選んだ実装([[GHC/Documentation]]など)にも最新の情報があるかもしれません。R
[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
Line 134: Line 114:
 
*[[Research_papers/Type_systems]] allow the curious to delve deeper.
 
*[[Research_papers/Type_systems]] allow the curious to delve deeper.
   
Languages: [[Types|en]]
+
Languages: [[Type|en]]

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