「Learn You a Haskell」の「Making Our Own Types and Typeclasses」の写経 その弐

Haskellで独自のデータ型、型クラスを定義する方法を知りたかったので [Learn You a Haskell for Great Good!](http://learnyouahaskell.com/chapters) の [Making Our Own Types and Typeclasses](http://learnyouahaskell.com/making-our-own-types-and-typeclasses) を写経してます。

今回は **「Record syntax」** です。

---

何か人物を表すデータ型を作成したいとします。保存したい情報は `first name`, `last name`, `age`, `height`, `phone number`, `favorite ice-cream flavor` です。

```haskell
data Person = Person String String Int Float String String deriving (Show)
```

１つ目のフィールドが `first name`、２つ目が `second name`、３つ目が `age` といった感じでしょうか。データを作成してみます。

```haskell
ghci>data Person = Person String String Int Float String String deriving (Show)
ghci>let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
ghci>guy
Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
```

問題なく作成できましたが少し読みづらいです。また、個別に情報を取り出す関数を作成したい場合はどうなるでしょうか？ `first name` や `last name` 取得するような関数です。このままでは下記のように定義する必要があります。

```haskell
firstName :: Person -> String
firstName (Person firstName _ _ _ _ _) = firstName

lastName :: Person -> String
lastName (Person _ lastName _ _ _ _) = lastName

age :: Person -> Int
age (Person _ _ age _ _ _) = age

height :: Person -> Float
height (Person _ _ _ height _ _) = height

phoneNumber :: Person -> String
phoneNumber (Person _ _ _ _ phoneNumber _) = phoneNumber

flavor :: Person -> String
flavor (Person _ _ _ _ _ flavor) = flavor
```

この実装を書くことは非常に面倒なものですが、一応動作します。

```haskell
ghci>let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
ghci>firstName guy
"Buddy"
ghci>height guy
184.2
ghci>flavor guy
"Chocolate"
```

データ型を書き込むためのもっと良い方法があります。それは **レコード構文** を使うことです。上記の機能を実現する方法を下記に示します。

```haskell
data Person = Person { firstName :: String
                    , lastName :: String
                    , age :: Int
                    , height :: Float
                    , phoneNumber :: String
                    , flavor :: String
                    } deriving (Show)
```

名前付けした型をスペースで区切る代わりに中括弧で囲みます。そしてフィールドの名前を書き、 `::` で区切って型を設定します。結果としてデータ型を同じになります。この主な利点はデータ型のフィールドを探索する関数を自動で作成してくれることです。レコード構文によってデータ型を作成することで、`firstName`, `lastName`, `age`, `height`, `phoneNumber`, `flavor` のような関数が自動で作成されます。

```haskell
ghci>:t flavor
flavor :: Person -> String
ghci>:t firstName
firstName :: Person -> String
```

レコード構文を使うことには他にも利点があります。独自の型に対し `Show` を導出させるときに、レコード構文を使用して型を定義してインスタンス化した場合には表示が異なります。

車を表現するデータ型があるとします。*その車を製造した会社*, *モデル名*, *製造年* を追跡したいとします。下記を見てください。

```haskell
data Car = Car String String Int deriving (Show)
```

```haskell
ghci>Car "Ford" "Mustang" 1967
Car "Ford" "Mustang" 1967
```

レコード構文を利用した場合には下記のようになります。

```haskell
data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)
```

```haskell
ghci>Car {company="Ford", model="Mustang", year=1967}
Car {company = "Ford", model = "Mustang", year = 1967}
```

新しく車を作成する際に、全てのフィールドをリストアップしておけば、フィールドの順序を意識する必要はありません。レコード構文を指定しない場合には順序を意識する必要があります。コンストラクターにフィールドが複数存在し、どのフィールドが何であるのか明らかにする必要がある場合はレコード構文を使います。

べクトルを表現するデータ型は `data Vector = Vector Int Int Int` というような定義であってもフィールドがベクトルの構成要素であることは一目瞭然ですが、`Person` や `Car` のようなデータ型ではレコード構文を使用することで構成要素を明らかにするできるという利点を得られます。

--- 

# まとめ

- データ型を作成するときフィールドの構成要素を明らかにしたい場合にはレコード構文が使える。
- レコード構文によって独自にデータ型を定義すると各フィールドを探索する関数を自動で作成してくれる
- データ型の定義にレコード構文を使用した場合、新たにモデル作成する際に全フィールドをリストアップしておけばフィールドの順序を意識しなくても良い。

次回に続きます。