パターンマッチングを使うと、表現力豊かな分岐コードを書くことができ、分割代入を使えば、値を変数にエレガントに束縛できます。
パターンマッチングでは、case文を使って、値を変数に束縛したり、ワイルドカード_で値を捨てたりできます。
type Entity = Alien | Stranger (Maybe String) | Friend { name: String }
hello : Entity -> String
hello entity =
case entity of
-- Custom type variant
Alien -> "Hello, you are not from around here are you?"
-- Binding to a litteral value nested in Stranger
Stranger (Just nametag) -> "Hello, erm... " ++ nametag ++ "."
-- Discarding Friend's data
Friend _ -> "Hi!"
-- All other cases
_ -> "Hello stranger!"
分割代入は、データの形が1つしかありえない場合に、let束縛や関数の引数、そしてもちろんcase式の中で値を束縛できます。
pairSum : ( Int, Int ) -> Int
pairSum pair =
-- Destructuring of a pair in a 'let' binding
let ( x, y ) = pair
in x + y
-- Destructuring in the function argument
pairSum : ( Int, Int ) -> Int
pairSum ( x, y ) = x + y
-- Custom type containing a single variant
type Container = Box String
-- Destructuring in the function argument
unbox : Container -> String
unbox (Box str) = str
-- Destructuring combined with pattern matching
unboxMaybe : Maybe Container -> Maybe String
unboxMaybe maybeContainer =
case maybeContainer of
Nothing -> Nothing
Just (Box "42") -> Just "The answer to the universe!"
Just (Box str) -> Just str
分割代入は、レコードにも、asキーワードと組み合わせても使えます。
type alias Circle =
{ radius : Float
, center : ( Float, Float )
}
perimeter : Circle -> Float
perimeter { radius } =
2 * pi * radius
left : Circle -> Float
left { radius, center } =
-- the pair center cannot be pattern matched directly in the function argument
-- so we do it in a 'let' statement
let ( x, _ ) = center
in x - radius
-- using the 'as' keyword to bind both the fields and the whole record
smaller : Float -> Circle -> Circle
smaller reduction ({ radius } as circle) =
if reduction < radius then
{ circle | radius = radius - reduction }
else
circle
ITヘルプデスクの一員として、チケットシステムをもっと効率的に改善し、上司に統計を提供できるようにしたいと考えています。
チケットにはステータス(new、in progress...)と作成者(ユーザー名と従業員IDで識別されます)があり、IT社員に割り当てることができ、コメントの履歴を含んでいます。
ここでは、現在のチケットシステムを改善する4つのタスクを紹介します。
空のコメントはインターフェースを煩雑にし、何の情報ももたらしません。それらを取り除きたいと考えています。
ユーザーとコメントのペアを受け取り、空のコメントに対してTrueを返すヘルパー関数emptyCommentを定義しましょう。
関数の引数で分割代入を使って、ユーザーとコメントを直接取り出します。ユーザーはその情報が必要ないので無視します。
emptyComment ( User "Alice", "" )
-- => True
上司は、チケットのプロセスにおけるユーザーの関与について統計を求めています。チケットの作成者が追加したコメントがいくつあるかを調べてほしいと考えています。
チケットを受け取り、チケットの作成者が追加したコメントの数を返すnumberOfCreatorCommentsを定義しましょう。
関数の引数で分割代入を使って、Ticket型からレコードを取り出すと同時に、レコードから必要なフィールドも取り出します。次に、let束縛の中でcreatedByを分解し、該当するコメントを数えられるようにします。数えるにはListの関数を使い、無名関数の引数で分割代入を使ってユーザーとコメントのペアを分けます。
numberOfCreatorComments
numberOfCreatorComments
(Ticket
{ status = Closed
, createdBy = ( User "Chiaki", 1 )
, assignedTo = Nothing
, comments = [ ( User "Chiaki", "Never mind, fixed it." ) ]
}
)
-- => 1
一部のチケットは、開発チームのAlice、Bob、Charlieにエスカレーションする必要があります。それらが進んでいるかを確かめるために、注意して見守ることが大切です。
チケットが開発チームのメンバーの誰かに割り当てられているかを確認するヘルパー関数assignedToDevTeamを定義しましょう。
関数の引数で分割代入を使って、Ticket型からレコードと必要なフィールドを取り出します。次に、1つのcase文を使い、再帰的なパターンマッチでユーザー名を取り出し、リテラルパターンマッチで照合して、チケットがAlice、Bob、Charlieの誰かに割り当てられているかを確認します。
assignedToDevTeam
(Ticket
{ status = InProgress
, createdBy = ( User "Bill", 2 )
, assignedTo = Just (User "Alice")
, comments = [ ( User "Bill", "What's an 'undefined'?" ) ]
}
)
-- => True
ときには、たくさんのチケットを他のIT社員に再割り当てする必要があります。
そのための関数assignTicketToを定義しましょう。この関数は、チケットを割り当てるユーザーとチケットという2つの引数を受け取ります。チケットのステータスがNewなら、新しいユーザーに割り当て、ステータスをInProgressに変更します。それ以外のステータスでは、単純にチケットを新しいユーザーに再割り当てします。ただし、ステータスがArchivedのチケットは一切変更しません。
関数の引数で分割代入を使って、Ticket型からレコードと必要なフィールドを取り出し、さらにasキーワードを使ってレコード全体をそのまま捕捉します。1つのcase文を使ってステータスをパターンマッチし、asキーワードで捕捉した元のレコードを変更して作成した、適切なチケットを返します。
assignTicketTo (User "Danny")
(Ticket
{ status = New
, createdBy = ( User "Jesse", 3 )
, assignedTo = Just (User "Alice")
, comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
}
)
-- => Ticket
-- { status = InProgress
-- , createdBy = ( User "Jesse", 3 )
-- , assignedTo = Just (User "Danny")
-- , comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
-- }