27
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

だれも Data.Maybe を教えてくれなかった

Last updated at Posted at 2018-11-22

はじめに

この数年、 Haskell (以下、 H) を独習している。 すごい H をはじめ数冊の本を読み、 web でもたくさんの良記事に出あった。残念ながらすごい H になりきれていない私だが、それでも H 生活に小さな幸せを見出している。

ところで、モジュール Data.Maybe は、私にとってはとても大切な H の道具箱なのだが、それについての記事を見た記憶がないのだ。なぜだ、なぜ書く人がいないのか。仕方がないので、私が語ることにする。すでに書いている人がいたらごめんなさい。

モジュール Data.Maybe

名前から想像できる通り、 Maybe にまつわる関数群である。 base に入っているので、特にインストールなど必要ない。使うには import Data.Maybe とすればよい。

以下, Data.Maybe -- hackage に載っているものを順番に見ていこう。 (リンクは base-4.12.0.0 のもの)

data Maybe a

Maybe
data  Maybe a  =  Nothing | Just a

みんな大好き Maybe の定義。ここで詳述するつもりはないが、シンプルであるにもかかわらず、 Functor とか Monad とか多くの関数型言語の概念にからみ、手続き型言語との差別化に大きな役割を果たしている。 H に欠かせないもの、それが Maybe だ。

maybe

maybe
maybe :: b -> (a -> b) -> Maybe a -> b

私はよく使う。第一引数が Nothing の時の default 値、第二引数が Just の中身に作用させる関数。

title.hs
import Safe (atMay)
-- atMay: 失敗をあつかえる (!!)。 成功時は Just にくるんで値を返し,
-- インデックスに対応する値がないと Nothing を返す

title "男" = "君"
title "女" = "ちゃん"
title _ = "さん"

nameWithTitle s =
  let ws = words s
  in (ws !! 1) ++ maybe "閣下" title (ws `atMay` 3)

main = do
  putStrLn $ nameWithTitle "山田 太郎 5 男"
  putStrLn $ nameWithTitle "田中 花子 4 女"
  putStrLn $ nameWithTitle "小暮 デーモン 100056" -- 性別フィールドがない
$ stack runghc title.hs
太郎君
花子ちゃん
デーモン閣下

isJust, isNothing

isJust,isNothing
isJust :: Maybe a -> Bool
isNothing :: Maybe a -> Bool

JustNothing かを判定するときに便利、なのだが、そんなに使わないのではなかろうか。これらの関数を filter とかと組みあわせるよりも、 maybe とか catMaybes とか使う方がより H だと思う。

fromJust

fromJust
fromJust :: Maybe a -> a

Haskell をはじめた頃、 Data.Map.lookup の結果だったか、これ絶対 Just v なのに、 v を取りだせない、ググっても取り外し方見つかんない、と腹立たしく思ったものである。本当は、 functor とか monad とか、なんらかの方法でNothing をちゃんと手当して、値がかぶっている Just の皮をやさしく剥いてあげなければならなかったのだ。いまならわかる。

だがしかーし、 fromJust を使うと簡単に Just を剥けてしまう。乱暴だと思うのだが、ちょっとした作業で絶対 Just だとわかっている場合は、これを使えばよいだろう。

なお、これで Nothing を無理矢理剥くと例外を発射してくるので注意。

seibetsu1.hs
import Safe (atMay)
import Data.Maybe

showSeibetsu s =
  let ws = words s
  in (ws !! 1) ++ " " ++ fromJust (ws `atMay` 3)

main = do
  putStrLn $ showSeibetsu "山田 太郎 4 男"
  putStrLn $ showSeibetsu "田中 花子 5 女"
  putStrLn $ showSeibetsu "小暮 デーモン 100056"
$ stack runghc seibetsu1.hs
太郎 男
花子 女
デーモン seibetsu1.hs: Maybe.fromJust: Nothing

fromMaybe

fromMaybe
fromMaybe :: a -> Maybe a -> a

maybe の特別な場合。もしくは丁寧な fromJustNothing に対して default 値を与えられる。やさしいあなたはこれでそっと Just を剥いてあげて。

seibetsu2.hs
import Safe (atMay)
import Data.Maybe

showSeibetsu s =
  let ws = words s
  in (ws !! 1) ++ " " ++ fromMaybe "悪魔" (ws `atMay` 3)

main = do
  putStrLn $ showSeibetsu "山田 太郎 4 男"
  putStrLn $ showSeibetsu "田中 花子 5 女"
  putStrLn $ showSeibetsu "小暮 デーモン 100056"
$ stack runghc seibetsu2.hs
太郎 男
花子 女
デーモン 悪魔

listToMaybe, maybeToList

listToMaybe,maybeToList
listToMaybe :: [a] -> Maybe a
maybeToList :: Maybe a -> [a]

Nothing と空リストを結びつけるコマンド。 listToMaybe は、最初はなんじゃこりゃ、と思ったのだが、みんなのなかで最初に成功したやつだけがえらい、そいつにしか興味がない、という状況はよくあって、意外に使える。一方、 maybeToList は私はあまり使っていないのだが、たぶん使っている人は多いんだと思うよ。

とまあ、こんなことを書いている私だが 、実は listToMaybe の代わりに Safe.headMay を使うことが多い模様。

catMaybes

catMaybes
catMaybes :: [Maybe a] -> [a]

かなり H な関数である、と個人的には思っている。

失敗する関数をデータに適用することについて考えてみよう。

命令型言語なら do ループの中に if 文とか置いて、「危険なの」と言われた時にはなにもしないようにすることだろう。 Haskell でもそのように書くことはできる。

catMaybes はちがう。後先考えずにやってしまい、まずいことがおきたら無かったことにしてしまう、そんなことを可能にする。こちらの考え方のほうがより H なのである。

catMaybes
catMaybes :: [Maybe a] -> [a]
showAge.hs
import Safe (readMay)
import Control.Applicative ((<$>))
import Data.Maybe

nameAndAge :: String -> Maybe (String,Int)
nameAndAge s =
  let ws = words s
  in ((,) (head ws)) <$> readMay (ws !! 2)

printTpl (n,i) = putStrLn $ n ++ " " ++ show i  

main = do
  mapM_ printTpl . catMaybes . map nameAndAge . lines 
    =<< readFile "meibo.txt" -- データ行のみならずヘッダーも含まれたテキストファイル

$ cat meibo.txt 
天使幼稚園 ガブリエル組 クラス名簿
姓 名 年齢 性別
山田 太郎 4 男
田中 花子 5 女
小暮 デーモン 100056
$ stack runghc showAge.hs
山田 4
田中 5
小暮 100056

mapMaybe

あーこれ使ったことないなー、今度使ってみよー、こんな文章書いておいて mapMaybe 使ったことないなんて恥ずかしー、まだまだ私 H じゃないわー。

さいごに

以上の Data.Maybe の関数は、どれも簡単なものである。できる人ならプログラム中に同じ効果のある snippet をさくさく埋め込むことができるだろう。なくても問題ないのだ。

以前、私はそのようなコードを書いていた。いま読み返すと、そんなコードは、なんとういか、ガバガバな感じがする。一方、 Data.Maybe を使うと、気持ちよく締まった、まことに H なコードが書ける。締まったコードを書くのに役立つモジュールは他にも Data.Either, Data.Bool などがあるが、 Data.Maybe は格別だ。

H 初心者の方、 Data.Maybe の使い方をおぼえてより H なコードを書いてほしい。そして、初心者に手ほどきをする立場のあなた、是非、 Data.Maybe を教えてより H になれるよう、優しく導いてあげていただきたい。

27
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?