git clone git://github.com/cohama/twittermonad
cd twittermonad
cabal sandbox init
cabal install --only-dependencies
cabal configure
cabal build
dist/build/twittermonad/twittermonad tl
# 初回は OAuth の認証が走るはず
dist/build/twittermonad/twittermonad tw "つぶやいてみる"
副作用をモナドで表現する
getStrLn :: IO String
putStrLn :: String -> IO ()
IO はいろいろできすぎてしまうので機能を限定したモナドを作る (DSL)
homeTimeLine :: Twitter [Tweet]
tweet :: String -> Twitter ()
fav :: TweetID -> Twitter ()
reply :: TweetID -> String -> Twitter ()
Twitter モナドに求める能力
- HTTP通信やファイル読み書き、その他もろもろ副作用っぽいこと =>
IO
- 設定情報を読む =>
Reader
- 失敗するかもしれない =>
Either
これらを Monad Transformer で積み上げる
newtype Twitter a = Twitter { unTwitter :: ExceptT String (ReaderT Conf IO) a }
deriving (Functor, Applicative, Monad, MonadIO, MonadReader Conf)
runTwitter :: Conf -> Twitter a -> IO (Either String a)
runTwitter c t = flip runReaderT c . runExceptT . unTwitter $ t
OAuth v1.1 です。 nonce や timestamp のせいで IO を使わざるを得ない
makeAuthorization :: String -- httpMethod (GET|POST)
-> String -- URL
-> [(String, Text)] -- parameters
-> BS.ByteString -- oauth consumer key
-> BS.ByteString -- oauth consumer secret
-> BS.ByteString -- oauth token
-> BS.ByteString -- oauth token secret
-> IO BS.ByteString
wreq というライブラリを利用。 レスポンスから Lens 経由で値を取り出せる