Haskell データ型の定義を学習する

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

データ型を定義する

dataキーワードで定義する。

data MyBool = False | True

MyBoolというデータ型はTrueまたはFalseの値コンストラクタを受け取る。型名及び、値コンストラクタは大文字から始まる必要がある。

値コンストラクタには引数を与えることができる。

data Shape = Circle Float Float Float |
  Rectangle Float Float Float Float

値コンストラクタは定義したデータ型を返す関数となる。

ghci> :t Circle
Circle :: Float -> Float -> Float -> Float -> Shape
ghci> :t Rectangle 
Rectangle :: Float -> Float -> Float -> Float -> Shape


定義したデータ型を使用する

Shape型の面積を求める関数。

area :: Shape -> Float
area (Circle _ _ r ) = pi * r ^ 2
area (Rectangle x1 y1 x2 y2) = ( abs $ x1 - x2 ) * ( abs $ y1 - y2 )

円を面積を求める場合、Circle型を引数としたいところなんだけど、Cirecleはデータ型ではなく値コンストラクタなので、引数として用いることはできない。True -> IntではなくBool -> Intと書かなければならない。ということ。

ghci> area $ Circle 1 1 1
3.1415927
ghci> area $ Rectangle 1 1 3 3
4.0


データ型に型クラスを定義する

Show型クラスを与える。(インデントに注意)

data Shape = Circle Float Float Float |
    Rectangle Float Float Float Float
  deriving (Show)
ghci> Circle 1.0 1.0 2.0
Circle 1.0 1.0 2.0


値コンストラクタは関数

値コンストラクタは関数なのでカリー化したり、高階関数として利用できたりする。とてもオシャレ。

ghci> let c = Circle 1.0 1.0 
ghci> map c [1,2,3]
[Circle 1.0 1.0 1.0,Circle 1.0 1.0 2.0,Circle 1.0 1.0 3.0]


値コンストラクタの引数にデータ型を与える

値コンストラクタの引数にデータ型を指定することも可能。Pointというデータ型を作成し、CircleとRectangleの座標点として活用。

data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float |
    Rectangle Point Point
  deriving (Show)


レコード構文

値コンストラクタの引数を名前付引数として表現する。

data Person = Person {
  firsrName :: String,
  lastName :: String,
  age :: Int
} deriving (Show)

指定したフィールド名は自動的に関数として定義される。モジュール化しておき関数名の衝突を防げばよい。

ghci> :t firsrName 
firsrName :: Person -> String

初期化。以下のように扱うことができる。

ghci> let p = Person {firsrName="Mike", age=19, lastName="Gonz"}
ghci> p
Person {firsrName = "Mike", lastName = "Gonz", age = 19}
ghci> firsrName p
"Mike"


型引数

値コンストラクタに与える引数型の推論に用いる。例えば以下のように記述すると、異なる型を値コンストラクタに指定することができる。

data AnyType a = AnyVal a deriving(Show)
ghci> let i = AnyVal 1
ghci> let s = AnyVal "string"
ghci> :t i
i :: Num a => AnyType a
ghci> :t s
s :: AnyType [Char]


データインスタンスの比較

Eq型クラスを指定しておく。

data Person = Person {
  firsrName :: String,
  lastName :: String,
  age :: Int
} deriving (Show, Eq)

比較はオブジェクト指向言語のようにインスタンスのハッシュ値を比較するのではなく、値コンストラクタと与えられた引数が一致するかで判断する。以下、p1とp2でTrueとなる。

ghci> let p1 = Person {firsrName="Mike",lastName="Gee",age=18}
ghci> let p2 = Person {firsrName="Mike",lastName="Gee",age=18}
ghci> let p3 = Person {firsrName="Mike",lastName="Gee",age=20}
ghci> p1 == p2
True
ghci> p1 == p3
False


列挙型

値コンストラクタに0引数、Enumを型クラスを与えることで列挙型が作成できる。OrdやBoundedも加えておくと便利。

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday    
  deriving (Eq, Ord, Show, Read, Bounded, Enum)
ghci> :t Monday 
Monday :: Day
Traversable  True
ghci> Monday == Tuesday 
False
ghci> minBound :: Day
Monday


型シノニム

ある型に別名をつける。typeキーワードで定義する。 String型[Char]の型シノニム。

type String = [Char]

型に別名をつけることによって、定義に対しての意味が明確になる。

type BookTitle = String
type Books = [(BookTitle, String)]
bookTitles :: Books -> [String]
bookTitles books = map (\(s, _) -> s) books

型シノニムに型変数を与えることもできる。

type AssocList k v = [(k, v)]


Eitherタイプ

LeftとRightの値コンストラクタとそれに与える違った型変数をとる。主にLeftが異常系、Rightが正常型のデータを表す。

data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)