昨夜書いたプログラムをリファクタリング

前回までと同じくHTTPのGETリクエストを投げるんだけど、ヘッダを追加してみました。

import Network.HTTP
import Network.HTTP.Base
import Network.URI

httpGet2 :: String -> IO (String)
httpGet2 url = do
  mayUri <- return $ parseURI url
  uri <- case mayUri of
           Nothing -> error ""
           Just  u -> return u
  req <- return $ Request {
                    rqURI = uri
                    ,rqMethod = GET
                    ,rqHeaders = [
                                   Header HdrAcceptCharset "Shift_JIS"
                                   ,Header HdrAccept "text/plain; charset=UTF-8"
                                   ,Header HdrAccept "text/html; charset=UTF-8"
                                 ]
                    ,rqBody = ""
                  }
  result <- simpleHTTP req
  res    <- return $ either (error . show) id result
  return $ rspBody res

一気に複雑に。でも増えたコードのほとんどはRequestの組み立て部分。

それはそうと、なんとなく「do記法を使ったら負け」みたいな強迫観念を持っているので>>=で書き換えてみました。

httpGet3 :: String -> IO (String)
httpGet3 url = (return $ parseURI url)
  >>= (\mayUri -> case mayUri of
                    Nothing -> error ""
                    Just  u -> return u
       )
  >>= (\uri -> return $ Request {
                          rqURI = uri
                          ,rqMethod = GET
                          ,rqHeaders = [
                             Header HdrAcceptCharset "Shift_JIS"
                             ,Header HdrAccept "text/plain; charset=UTF-8"
                             ,Header HdrAccept "text/html; charset=UTF-8"
                          ]
                          ,rqBody = ""
                        })
  >>= simpleHTTP
  >>= return . either (error . show) id
  >>= return . rspBody

・・・いまいち。do記法を使いたくないばかりに無名関数を使わないといけなくなったり $ と . が混在したりと、かなり統一感に欠けるコードになってしまいました。
こんなときは関数分割が足りないのだ、とばかりにリファクタリング

httpGet4 :: String -> IO (String)
httpGet4 url = (return . fromJust . parseURI) url
         >>= return . getRequest'
         >>= simpleHTTP
         >>= return . rspBody . (either (error . show) id)
    where
      getRequest' :: URI -> Request String
      getRequest' uri = Request {
                          rqURI = uri
                          ,rqMethod = GET
                          ,rqHeaders = [(Header HdrAcceptCharset "UTF-8")
                                        , (Header HdrAccept "text/plain; charset=UTF-8")
                                        , (Header HdrAccept "text/html; charset=UTF-8")]
                          ,rqBody = ""
                        }

おお、$が消えた!
ついでに、こっそりMaybeをパターンマッチしてた箇所をData.MaybeのfromJustに置き換えたりムダなreturnをなくしたりして、かなりすっきりさせることが出来ました。
これなら読める。


>>=を使うと引数が消えるので記述がスリムになるのですが、計算の途中で引数が使えないのが困ります。do記法を使えばこの辺りは解消しますが、小さい関数の組み合わせで大きい関数を作っていく、というHaskellの思想(というより関数型言語の思想か?)に沿うなら、単純な機能を持つ関数に切り出して>>=ですっきり書ける工夫をした方がかっこいいと思うのです。。。