慢性上咽頭炎の近況
初めて塩化亜鉛療法(Bスポット)を受けてから2ヶ月ほどが経ったのでここで現状を記録しておく。
この2ヶ月、毎週Bスポットを受けたが、症状は若干良くなった程度である。まあどのみち時間がかかるものだと覚悟しているので特にガッカリしているわけではない。
若干良くなった程度、と言ったが、以前よりは目まいを感じている時間は短くなっているとは思う。以前は長い会議の後などにかなりの目まいがあったが、そういうことは減ったと思う。また、休日はほとんど目まいを感じなくなった。
逆に目まいを感じやすいのは昼食後の眠くなる時間帯である。この時間がヤバい。アメなど、甘いものを食べるとさらに酷くなるが、コーヒーを飲むとマシになる。眠気と連動しているような気がする。また、立ってものを食べるのも目まいを引き起こすように思う。
続きを読むHaskellでいい感じのHTTPパーサライブラリはない?
「いい感じの」とはずいぶん主観的だが、だいたい以下のようなHTTPパーサが欲しくなってちょっと調べていた。
- 基本的に(Lazy?)ByteStringに対して作用する。
- 非IO。
- HTTPリクエストとレスポンスをパースできる。
- HTTPボディも引っこ抜いてくれる。
- HTTPボディは一定メモリでストリーミングできる (具体的な実現形態はうまくイメージできないが・・)
- Transfer-Encoding: chunkedなHTTPボディをデコードしてストリーミングできる。
- 一つのByteStringに複数のHTTPリクエスト or レスポンスが含まれている場合もパースできる。
- できればContent-Encoding: gzipなどの圧縮コンテンツを解凍するオプションがある。
- さしあたりHTTP/2は非対応でいい。
イメージとしてはこちらで質問されているようなものが近い。
が、意外とこういうものがないかも? 少し調べてみた。
salvia-protocolパッケージの提供するパーサ。Stringがターゲットなのでボツ。
http-clientの内部モジュール。
'parseStatusHeaders'や'getResponse'関数があるが、いずれもConnectionをターゲットとしたIO関数。
attoparsecのexamplesに同梱されているパーサ。
見たところContent-Lengthヘッダの解釈などもやっていないようなのでボツ。
warpの内部モジュール。
'recvRequest'関数があるが、やはりConnectionをターゲットとしたIO関数。そこから呼ばれている'parseRequest'関数などもIO。
ということでHTTPクライアント、サーバの内部実装を簡単に調べてみたが、意外とIOに密結合した作りになっているようだ。ボディストリーミングをやる都合上か、性能を高める都合上そうなっているのかもしれない。
別にIOでも構わないといえば構わないが、本質的にIOじゃなくてよいものをIOにするのはなんとなくモヤる。
他に何かないだろうか。
lifted applicative style
モナドを使った演算を書いている時は、applicative styleを使うと記述量を少なくできて便利だ。
>>> (++) <$> getLine <*> getLine hoge foobar "hogefoobar"
使う関数が最終的にモナドを返す場合、最後にjoinしてやればいい。
>>> let catPut a b = putStrLn ("== " ++ a ++ b) >>> join $ catPut <$> getLine <*> getLine hoge foobar == hogefoobar
さて、上記と同様のことをmonad transformerで拡張した文脈中ではどうするか、ちょっと悩んでしまった。
正解は(lift =<<)を使えばいい。
>>> let action = lift =<< catPut <$> ask <*> lift getLine >>> runReaderT action "hoge" foobar == hogefoobar
(lift =<<)の型はこうだ。
>>> :t (lift =<<) (lift =<<) :: (Monad (t m), Monad m, MonadTrans t) => t m (m b) -> t m b
見ての通り、join :: Monad m => m (m b) -> m b によく似ている。
ちなみにcatPutへの引数がモナドではない素のデータだとこうなる。
>>> let action' = lift $ catPut "HOGE" "FOOBAR" :: ReaderT String IO () >>> runReaderT action' "hoge" == HOGEFOOBAR
素のデータの場合の記法から、($)を(=<<)に、関数適用を(<$>)と(<*>)に書き換えればapplicative styleになる。
()は(.)の代わりに使える
例えばこういう関数群があったとして、
toStr :: Float -> String toStr = show length' :: String -> Int length' = length isBig :: Int -> Bool isBig = (> 5)
Haskellでは($)演算子を使ってデータに対して次々に関数を適用できる。
isBig $ length' $ toStr $ 32.1
($)演算子は右結合なので、以下のように結合している。
isBig $ (length' $ (toStr $ 32.1))
逆に、左結合を強制すると型が合わずにエラーになる。
((isBig $ length') $ toStr) $ 32.1 -- エラー
さて、関数に流し込みたいデータがFunctorの中に入っている場合、Control.Applicativeモジュールの(<$>)演算子が便利である。
isBig <$> length' <$> toStr <$> Just 32.1
しかし(<$>)演算子は左結合である。つまり以下のように結合していることになる。
((isBig <$> length') <$> toStr) <$> Just 32.1
しかも(<$>)演算子の場合、右結合を強制してもちゃんと動作する。
isBig <$> (length' <$> (toStr <$> Just 32.1))
なぜ($)と違って左結合で動作するのだろうか。
ここで、(isBig <$> length')の型を調べてみる。
*Main> :i isBig <$> length' isBig :: Int -> Bool -- Defined at ops.hs:11:1 (<$>) :: Functor f => (a -> b) -> f a -> f b -- Defined in `Data.Functor' infixl 4 <$> length' :: String -> Int -- Defined at ops.hs:8:1 *Main> :t isBig <$> length' isBig <$> length' :: String -> Bool
(<$>)の第2引数はlength'という関数である。そして関数はFunctorである。つまりこの場合、(<$>)は
(<$>) :: (Int -> Bool) -> (->) String Int -> (->) String Bool
このような型に束縛されていて、結果的に(String -> Bool)という型を返したのだ。
ちなみに上記の型を一般化すると以下のようになる。
(<$>) :: (b -> c) -> (->) a b -> (->) a c
これは関数合成演算子(.)と同じである。
*Main> :i (.) (.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base' infixr 9 .
必須引数とオプショナル引数が混在した名前付き引数渡し
Haskellで名前付き引数(named arguments/parameters)のようなことをしようと思ったらレコード構文を活用するのが一般的である。
data Args = Args { arg1 :: Int, arg2 :: Int, arg3 :: String } func :: Args -> Int func = undefined main = print $ func $ Args { arg1 = 100, arg2 = 50, arg3 = "foobar" }
この際、デフォルトのArgsレコードを用意してHaskellのレコード更新構文を使うことで、引数をoptionalにすることができる。
defArgs :: Args defArgs = Args { arg1 = 0, arg2 = 0, arg3 = "" } main = print $ func $ defArgs { arg2 = 99 }
更新されていないフィールドにはデフォルトの値が用いられる。
data-defaultパッケージへ依存しても構わないのであれば、Default型クラスを使うとより利便性が向上するだろう。
import Data.Default (Default(def)) instance Default Args where def = defArgs main = print $ func $ def { arg2 = 99 }
全ての引数がoptionalであればこれで構わない。しかし、一部の引数を必須(mandatory)としたい場合、どうすればよいだろうか。つまり、
data Args = Args { m1 :: Int, m2 :: Int, o1 :: String }
このような引数構成で、m1とm2は必須、o1はオプショナルとしたい場合である。
まず、先述のdefArgsに引数を取らせる方法が考えられる
data Args = Args { m1 :: Int, m2 :: Int, o1 :: String } defArgs :: Int -> Int -> Args defArgs m1_val m2_val = Args { m1 = m1_val, m2 = m2_val, o1 = "" } func :: Args -> Int func = undefined main = print $ func $ defArgs 4 5
しかしこれでは必須引数が結局positional argumentになってしまい、必須引数が増えた場合に可読性が低下する。
必須引数をundefinedとする手もある。
import Data.Default (Default(def)) data Args = Args { m1 :: Int, m2 :: Int, o1 :: String } instance Default Args where def = Args { m1 = undefined, m2 = undefined, o1 = "" } func :: Args -> Int func = undefined main = print $ func $ def { m1 = 10, m2 = 20 }
こうしておけば、必須引数を設定し忘れた場合、その値を参照しようとした段階で実行時例外が発生する。
しかし、この方法では必須引数の設定し忘れをコンパイル時に検出できない。Haskellを書いている以上、プログラムの正しさはなるべくコンパイル時に検証したいものである。
そうなると、やはり必須引数とoptional引数を分けたほうがいいのだろうか。
import Data.Default (Default(def)) data Args = Args { m1 :: Int, m2 :: Int } data Opts = Opts { o1 :: String } instance Default Opts where def = Opts { o1 = "" } func :: Args -> Opts -> Int func = undefined main = print $ func Args { m1 = 10, m2 = 20 } def
上の例では、必須引数をArgs型に、optional引数をOpts型にまとめ、Opts型のみをDefaultのインスタンスにしている。また、func関数はこの2つを引数としてとる。Args型データをレコードコンストラクタで作成する際にフィールドの数が足りない場合、ghcは警告を発する。