Haskellerへの道 #8 - データ型

Last Edited: 6/20/2024

このブログ記事では、Haskellにおいて重要なデータ型の定義方法について紹介しています。

Haskell & Datatypes

データ型

私たちは、リスト、BoolStringIntegerのような多くの事前に定義されたデータ型を見てきましたが、 自分自身でデータ型を定義することもできます。Boolがどのように定義されているかを見て、新しいデータ型の定義方法を理解しましょう。

--- 新しいデータ型の定義
data Name = 
    Constructor1 <args> | Constructor2 <args> ...
--- Name: 新しいデータ型の名前
--- Constructor 1,2,...: 値コンストラクタ
--- <args>: コンストラクタの引数
 
data Bool = True | False

Boolはデータ型であり、TrueFalseは値コンストラクタです。値コンストラクタとは0個以上のパラメータ/引数を取り、データ型の値を返す関数を指します。 TrueFalseの場合、パラメータを取らないので、それ自体がBool型の値となります。

コンストラクタが0個以上のパラメータを取る関数であるため、次のようなこともできます。

data Calculation = 
   Add Int Int | Subtract Int Int | Multiply Int Int | Divide Int Int

CalculationのすべてのコンストラクタはInt型の2つの値を取り、Calculation型の値を返します。GHCiで コマンドを使ってコンストラクタの型を見てみましょう。

ghci> :t Add
Add :: Int -> Int -> Calculation
ghci> :t Subtract
Add :: Int -> Int -> Calculation

上記のように、値コンストラクタが関数であることがわかりますので、次のようなこともできます。

map (Add 1) [1,2,3] --- Output: [Add 1 1, Add 1 2, Add 1 3]

Calculationデータ型とそのコンストラクタは、次のようにパターンマッチングに使用できます。

calc :: Calculation -> Int
calc (Add x y) = x + y
calc (Subtract x y) = x - y
calc (Multiply x y) = x * y
calc (Divide x y) = div x y

再帰的データ型

Haskellでは、再帰を愛することを学ぶ必要があります。値コンストラクタは再帰的に、 自分の型の値を取り、それによって新しい値を作ることができます。いくつかの例を見て理解してみましょう。

data PeaNum = 
    Succ PeaNum | Zero
 
data Tree a =
    Leaf | Node (Tree a) a (Tree a)

PeaNumTreeの両方には、引数を持たない値コンストラクタ(ZeroLeaf)と、自分の型の値を引数として取る値コンストラクタがあります。 これにより、より柔軟なデータ型を作成できます。ここで、Treeにはその定義に型変数aがあることに注意してください。 データ型を通して引数の型を1つに限定したいが、使用する型を指定したくない場合は、Treeのように定義に型変数を入れれば良いです。

これらの再帰的データ型をどのように使用できるのでしょうか?基本的には通常のデータ型と同じです。

--- 変数fiveにPeaNumの値を定義
five = Succ $ Succ $ Succ $ Succ $ Succ Zero
 
--- PeaNumのインクリメント
incr :: PeaNum -> PeaNum
incr = Succ --- 部分適用関数
 
--- PeaNumのデクリメント
decr :: PeaNum -> PeaNum
decr (Succ n) = n --- Succの分解
 
--- 2つのPeaNumを加算
add :: PeaNum -> PeaNum -> PeaNum
add Zero n = n
add (Succ m) n = Succ $ add m n --- Succの分解と$による構文
 
--- リスト内のPeaNumの合計を取得
sumPeaNums :: [PeaNum] -> PeaNum
sumPeaNums [] = Zero
sumPeaNums (x:xs) = add x $ sumPeaNums xs --- 上記のadd関数を使用
 
--- Intへの変換
count :: PeaNum -> Int
count Zero = 0
count (Succ n) = 1 + count n

Haskellのデータ型に関するルールは他にもたくさんありますが、今回はこれで終わりにしましょう。

クイズ

この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。

リソース