たとえば、コーヒーメニューアイテムの名前とその価格があったときに、値段が高い方を取り出したい、とする。 ただし、価格が不明なアイテムもあるので、価格の型は Mayb Int になっている。 それらを上手に取り扱いたい場合を考える。
テストするデータモデル:
type Price = Maybe Int
data Item = Item String Price deriving (Show)
itemCA = Item "Caffe Americano" (Just 400)
itemCM = Item "Caffe Misto" (Just 500)
itemPPR = Item "Pike Place Roast" Nothing
このように価格が不明なアイテムが紛れ込んでいる場合に備えて、そこを Maybe Int 型にしておく。 このようなデータから2つのアイテムを取り出して価格比較を行いたい。
そのために、2つのアイテムを適用すると値段が高い方を返す関数を考える。 一方または両方の価格が不明な場合を想定し、そのときは、Nothing を返せるように 返り値の型を Maybe Item とする。
higherPriceItem :: Item -> Item -> Maybe Item
価格が不明な場合には 価格を 0 と見なして処理するコードを考えました。
higherPriceItem item0 item1
| (price item0) == Nothing = Nothing
| (price item1) == Nothing = Nothing
| (maybe 0 id (price item0)) < (maybe 0 id (price item1)) = Just item1
| otherwise = Just item0
where
price :: Item -> Price
price (Item _ v) = v
ghci で確認:
> :load price.hs
> higherPriceItem itemCA itemCM
Just (Item "Caffe Americano" (Just 400))
> higherPriceItem itemCA itemPPR
Nothing
それぞれのMaybe Int から Int を取り出して比較して価格が高い方を返す部分に do 構文を使う場合。
higherPriceItem :: Item -> Item -> Maybe Item
higherPriceItem item0 item1 = do
price0 <- price item0
price1 <- price item1
if price0 < price1
then Just item0
else Just item1
where
price :: Item -> Price
price (Item _ v) = v
方法A と比べての利点としては、Nothing の場合の対処をいちいち記述しないで済む点。
完成したコード: price.hs
type Price = Maybe Int
data Item = Item String Price deriving (Show)
itemCA = Item "Caffe Americano" (Just 400)
itemCM = Item "Caffe Misto" (Just 500)
itemPPR = Item "Pike Place Roast" Nothing
higherPriceItem :: Item -> Item -> Maybe Item
{-
higherPriceItem item0 item1
| (price item0) == Nothing = Nothing
| (price item1) == Nothing = Nothing
| (maybe 0 id (price item0)) < (maybe 0 id (price item1)) = Just item1
| otherwise = Just item0
where
price :: Item -> Price
price (Item _ v) = v
-}
higherPriceItem item0 item1 = do
price0 <- price item0
price1 <- price item1
if price0 < price1
then Just item0
else Just item1
where
price :: Item -> Price
price (Item _ v) = v