Difference between revisions of "型"
Ymotongpoo (talk | contribs) |
Ymotongpoo (talk | contribs) |
||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | Haskellでは '''型''' とはプログラム内で用いるデータを表現するものです。 |
||
− | In Haskell, '''types''' are how you describe the data your program will work with. |
||
[[Category:Language]] |
[[Category:Language]] |
||
Line 5: | Line 5: | ||
==データの宣言== |
==データの宣言== |
||
+ | Haskellでは型を<code>data</code>宣言を通じて導入、または宣言します。一般的にデータ宣言はこのように行います。 |
||
− | One introduces, or declares, a type in Haskell via the <code>data</code> statement. In general a data declaration looks like: |
||
data [context =>] type tv1 ... tvi = con1 c1t1 c1c2... c1tn | |
data [context =>] type tv1 ... tvi = con1 c1t1 c1c2... c1tn | |
||
Line 11: | Line 11: | ||
[deriving] |
[deriving] |
||
+ | まだHaskellをあまり理解していいない方にはおそらく説明になっていないかもしれません。 |
||
− | which probably explains nothing if you don't already know Haskell! |
||
+ | 上の宣言の本質は、<hask>data</hask>キーワードを使って、付属的なコンテキストを与え、型の名前と多くの[[type variable]]を与えることにあります。 |
||
− | The essence of the above statement is that you use the keyword <hask>data</hask>, |
||
+ | その後、多くの[[constructor]]が続きます。これらは[[type variable]]か[[type constant]]のリストになっています。最後に付属的に<code>deriving</code>が続きます。 |
||
− | supply an optional context, give the type name and a variable number of |
||
− | [[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>. |
||
+ | 他にも多くの微妙な事柄が必要です。たとえばデータコンストラクタに必要なパラメータは[[eager]]でなければならない、どんな[[Type class|class]]が[[deriving]]の中で許可されているか、コンストラクタ内の[[field]]名の使い方、[[context]]が実際になにをするのかなどです。これらについてはそれぞれの記事を参照してください。 |
||
− | There are a number of other subtleties associated with this, such as requiring |
||
− | 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. |
||
+ | いくつかの例を見てみましょう。Haskellの標準データ型[[Maybe]]は普通このように宣言されています。 |
||
− | Let's look at some examples. The Haskell standard data type [[Maybe]] is typically declared as: |
||
<haskell> |
<haskell> |
||
data Maybe a = Just a | Nothing |
data Maybe a = Just a | Nothing |
||
</haskell> |
</haskell> |
||
+ | これは、'''Maybe'''型は''a''で表される1つの型変数を持っていて、'''Just'''と'''Nothing'''という2つの[[constructor]]を持っているということを意味しています。(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''. |
||
+ | '''Just'''コンストラクタは1つのパラメータ"a"をとります。 |
||
+ | 他の例として、二分木 (binary [[Tree]])を考えてみましょう。このように表されます。 |
||
− | As another example, consider binary [[Tree]]s. They could be represented by: |
||
<haskell> |
<haskell> |
||
data Tree a = Branch (Tree a) (Tree a) | Leaf a |
data Tree a = Branch (Tree a) (Tree a) | Leaf a |
||
</haskell> |
</haskell> |
||
+ | ここで、'''Tree'''のコンストラクタの1つ'''Branch'''はコンストラクタのパラメータには2つのtreeを取る一方で、'''Leaf'''は型変数"a"だけを取ります。Haskellではこういった再帰型は非常によく使われる[[:Category:Idioms |patterns]]です。 |
||
− | Here, one of the constructors, '''Branch''' of '''Tree''' takes two trees as |
||
− | parameters to the constructor, while '''Leaf''' takes the type variable ''a''. This type of recursion is a very common [[:Category:Idioms |pattern]] in Haskell. |
||
==型と新しい型== |
==型と新しい型== |
||
+ | 他のHaskellプログラムに型を導入する方法は<hask>type</hask>と<hask>newtype</hask>を用いて行う方法です。 |
||
− | The other two ways one may introduce types to Haskell programs are via the |
||
− | <hask>type</hask> and <hask>newtype</hask> statements. |
||
+ | <hask>type</hask>はあるデータコンストラクタに対してシノニムを与えます。<hask>newtype</hask>ではある型をリネームして新しいコンストラクタを作成する必要があります。 |
||
− | <hask>type</hask> introduces a synonym for a type and uses the same data |
||
− | constructors. <hask>newtype</hask> introduces a renaming of a type and |
||
− | requires you to provide new constructors. |
||
+ | <hask>type</hask>宣言をするときは、型シノニムとその基底型はほぼいつでも交換可能です。([[instance]]宣言を扱う場合にいくつか制限があります)たとえば、このような宣言があったとします。 |
||
− | When using a <hask>type</hask> declaration, the type synonym and its base type |
||
− | 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> |
||
+ | <hask>String</hask>をシグネチャにもつどのような[[関数]]も<code>Name</code>型の要素に対して用いることができます。 |
||
− | then any [[function]] you had declared that had <hask>String</hask> in its |
||
− | 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> |
||
+ | 先ほどの例のようにはいきません。関数の宣言においては実際に'''FirstName'''に対して定義されてなければいけません。このような要求を軽減するために、しばしばデコンストラクタを宣言します。こんな具合です: |
||
− | this would no longer be the case. Functions would have to be declared that |
||
− | 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> |
||
+ | これはよく<code>newtype</code>内の[[field]]を使うときに行われます。(フィールドを幅広く使う人もいる一方で、多くの人がHaskellのフィールド実装は甘いと考えています。[[Programming guidelines]]や[[Future of Haskell]]も参照してください) |
||
− | This is often done by the use of [[field]]s in the <code>newtype</code>. (Note |
||
− | 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> |
||
+ | この例ではスートと数字を / から[[String]]やIntに変換できるように[[deriving]]節を使っています。これによって同値性や順序を確認できます。型変数がないような型を使うことで、同値性はどのコンストラクタが使われたかに依存し、順序は記述の順番に依存します。例えば<code>Three</code>は<code>Queen</code>よりも小さいです。 |
||
− | 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, |
||
− | 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>. |
||
− | + | では実際に<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> |
||
+ | この定義では[[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>. |
||
− | + | ここでの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> |
||
+ | 最後に、<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 116: | 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 |
||
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: [[ |
+ | 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 variableかtype constantのリストになっています。最後に付属的にderiving
が続きます。
他にも多くの微妙な事柄が必要です。たとえばデータコンストラクタに必要なパラメータはeagerでなければならない、どんなclassがderivingの中で許可されているか、コンストラクタ内のfield名の使い方、contextが実際になにをするのかなどです。これらについてはそれぞれの記事を参照してください。
いくつかの例を見てみましょう。Haskellの標準データ型Maybeは普通このように宣言されています。
data Maybe a = Just a | Nothing
これは、Maybe型はaで表される1つの型変数を持っていて、JustとNothingという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プログラムに型を導入する方法はtype
とnewtype
を用いて行う方法です。
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 guidelinesやFuture 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節を使っています。これによって同値性や順序を確認できます。型変数がないような型を使うことで、同値性はどのコンストラクタが使われたかに依存し、順序は記述の順番に依存します。例えばThree
はQueen
よりも小さいです。
では実際にCard
を定義してみましょう。
data Card = Card {value::CardValue,
suit::Suit}
deriving (Read, Show, Eq)
この定義ではfieldを使っています。これによってCard
の2箇所に対してアクセスする既成の関数が手に入ります。また、もう一度いいますが、型変数(type variables)が使われていませんが、データのconstructorは2つの引数に対して特定の型、つまりCardValue
とSuit
であることを要求しています。
ここでのderiving節は単に求められている3つのClassを明記しているだけです。instance宣言をOrdやEnumに対して行います。
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]]
追加してください
より図解が多い例を歓迎します。
参考
データconstructorやclassに関する(必要な)記事を読んでみてください。またHaskell 98 reportやご自分が選んだ実装(GHC/Documentationなど)にも最新の情報があるかもしれません。R
- Determining the type of an expression - Let the Haskell compiler compute the type of expression
- Language extensions - many language extensions are to do with changes to the type system.
- Smart constructors shows some interesting examples including a non-trivial usage of
newtype
. - Unboxed type shows ways to have values closer to the bare metal :).
- Phantom type discusses types without constructors.
- Type witness gives an example of GADTs, a GHC extension.
- Existential type shows how to implement a common O-O programming paradigm.
- Type arithmetic implements the Peano numbers.
- Reified type, Non-trivial type synonyms, Abstract data type, Concrete data type, Algebraic data type.
- Research_papers/Type_systems allow the curious to delve deeper.
Languages: en