Maybe FizzBuzzの実装比較: HaskellとClojureに関して
この記事はHaskell Advent Calender 2013 13日目の記事の筈でした。
この記事は間違った記事の可能性があります (2013/12/17 追記)
Maybe Monadsが適切に使われていないというご指摘を頂いています。修正までにお時間をください。
Maybe Monadsの例は不適切だった可能性があります (2013/12/18 追記)
このようなフォロー(?)を頂きました。
@esehara Maybe モナドの記事、もしかしたらこんな感じのことを表現したかったのかなーって思ってみたりしてみたでし https://t.co/crS7QvECfZ
— (deffish *tai*) (@taiki45) 2013, 12月 17
ありがとうございます。以下については、間違いがあるので、気をつけてお読みください。
はじめに
関数型言語界隈には、安易に触れると燃えるトピックというのが存在します。その一つとして「モナド」があります。そして、それが余りにも綺麗なために、他の関数型言語を標榜する言語でも、モナドを取り入れたアプローチが導入され、そして失敗します。
来年の課題言語として、自分はClojureを選んでいます。そして、当たり前の如く、Clojureの公式グループが内包しているライブラリの一つに、algo.monadsというものが存在しています。
自分は、何らかのパラタイムを試す時に、とりあえずFizzBuzzを書くというのをやっています。プログラミング課題におけるFizzBuzzとは何か、については検索して頂ければわかるかと思われます。今回の記事については、CLojureとHaskellのMaybe FizzBuzz
の実装を通じて、モナドについての足がかりを自分なりに手に入れることができれば、とおもっています。
モナドでFizzBuzz(Haskell)
では、まずHaskellでMaybe Monads
によるFizzBuzzを書いてみましょう。
import Control.Monad ((>=>)) maybe_fizz :: Integer -> Maybe String maybe_fizz 0 = Just "Fizz" maybe_fizz x = Just "" maybe_buzz :: Integer -> String -> Maybe String maybe_buzz 0 x = Just $ x ++ "Buzz" maybe_buzz _ x | x == "" = Nothing | otherwise = Just "Buzz" maybe_fizzbuzz :: Integer -> Maybe String maybe_fizzbuzz x = maybe_fb (x `mod` 3) where is_buzz = maybe_buzz (x `mod` 5) maybe_fb = maybe_fizz >=> is_buzz maybe_process :: Integer -> String maybe_process x = case (maybe_fizzbuzz x) of Nothing -> show x Just str -> str main :: IO () main = go maybe_process
かなり汚いコードですが、FizzBuzz
の過程を副作用の連鎖として設計した場合のコードです。つまり、あるモナドが割り切れた場合においては、何らかの文字列が入り、そしてあるモナドが割り切れない場合においては、Nothing
が入ったままになります。Nothing
の場合、文字列がないわけですから、数字を返し、文字列がある場合は、その副作用としての文字列を取り出しています。
さて、これをRoyという、関数型JavaScript Translaterに内包されているMonad Syntax
において書き直してみましょう。
data Option a = Some a | None let is_fizz x = if x % 3 == 0 then Some "Fizz" else None () let is_buzz x m = if x % 5 == 0 then match m case (Some a) = Some (a ++ "Buzz") case None = Some "Buzz" else m let optionMonad = { return: \x -> Some x bind: \x f -> match x case (Some a) = f a case None = None () } let fizzbuzz x y = if x > y then console.log("") else let fizz = is_fizz x let result = (do optionMonad buzz <- is_buzz x fizz return buzz ) match result case (Some a) = console.log a case None = console.log x fizzbuzz (x + 1) y fizzbuzz 1 100
ScalaのMore Functionalなアプローチを支援するScalazを使ったMaybe FizzBuzzも書いてみたかったのですが、誰か有志が書いてくれる気がしまず。
モナドの定義
モナドの定義に関しては、自分が説明するよりも、以下の説明を引用するだけで十分でしょう。
で、モナドって使えるの?
インターネット上のドキュメントについて、例えば「何をモナドとしてよぶことが出来るのか」と、「モナドという考え方を通じることによってどのようなアプローチを考えることができるのか」ということについては、若干の混乱があるようにあります。
自分が調べた限りだと、ある機能の担保自体は、モナドとは関係はなく、しかし現実問題として、例えばIO制御であったり、あるいは副作用をハンドリングする上において、モナドというパターンを利用すると静的型付き関数型言語において、上手く抽象化出来るという風に捉えてます。
そして、さらに言うとモナドという考え方が有用である場合と有用ではないパターンがあります。
Clojureにおけるモナド
ではClojureの場合はどうでしょうか。例えば、有名なプログラミング質問サイトにおける解答の一部を紹介します。
ベストアンサーのざっくりとした超訳
そもそもClojureって動的型付け言語だから、君が「文字列が欲しいなあ」と思ったら文字列が渡されるし、nilが欲しいなあ、と思えばnilが返ってくるんだよね。「関数型」って、別に「コンパイル時における厳密な型付け」を意味しているわけではないし、それってHaskellを書いているときのアプローチと全く逆の経験なんだと思うよ。
何を言っているのでしょうか。
そこで、上のことを考えるために、algo.monads
を使って、Maybe FizzBuzz
を考えてみましょう。
(defn if-n [num string] (fn [int] (domonad maybe-m [:when (zero? (mod int num))] string))) (def fizz (if-n 3 "Fizz")) (def buzz (if-n 5 "Buzz")) (with-monad maybe-m (def m+ (m-lift 2 str))) (defn m-merge [m1 m2] (domonad maybe-m [has-value-m1 (not (nil? m1)) has-value-m2 (not (nil? m2)) :when (or has-value-m1 has-value-m2)] (if (and has-value-m1 has-value-m2) (m+ m1 m2) (m-plus m1 m2)))) (defn do-fb [f n] (domonad maybe-m [fizzbuzz-m (f n) :when (not (nil? (f n)))] fizzbuzz-m)) (defn fizzbuzz [n] (with-monad maybe-m (m-plus (m-merge (do-fb fizz n) (do-fb buzz n)) (m-result n))))
一部、Clojureのalgo.monads
は使い勝手が悪い、という印象があるようです。自分もそのように考えます。
例えば、Maybe Monads
の役割の一つには、Null Pointerみたいな、いわゆるクリティカルな値を適切に処理するという問題意識があったかと思われますが、Clojureの場合、普通にmonadでバインドしていたとしても、各要素に対して普通にnil
として扱えてしまいます。モナドにおいて、恐らく動的型付けという部分において、有用性が一部減少してしまう事実はあるのかもしれません。それはClojureにおいても似ているかもしれません。
Clojure Communityにおいて、今後ライブラリとして、型チェックを厳密にするライブラリを改善し、実用に耐えうるレベルに持っていく計画がされています。そのときになったら、Clojureにおけるモナドも、もっと使い勝手がよくなるのかもしれません。
もっと詳しく知りたい
というわけで、下の本をお薦めしておきます。

- 作者: Richard Bird,山下伸夫
- 出版社/メーカー: オーム社
- 発売日: 2012/10/26
- メディア: 単行本(ソフトカバー)
- 購入: 3人 クリック: 28回
- この商品を含むブログ (4件) を見る

- 作者: Benjamin C. Pierce,住井英二郎,遠藤侑介,酒井政裕,今井敬吾,黒木裕介,今井宜洋,才川隆文,今井健男
- 出版社/メーカー: オーム社
- 発売日: 2013/03/26
- メディア: 単行本(ソフトカバー)
- クリック: 68回
- この商品を含むブログ (9件) を見る