Haskell を使用して Web ページをスクレイピングし、その結果をオブジェクトにコンパイルしようとしています。
何らかの理由でページからすべての項目を取得できない場合は、ページの処理を中止して早期に戻るようにします。
例えば:
scrapePage :: String -> IO ()
scrapePage url = do
doc <- fromUrl url
title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText
when (isNothing title) (return ())
date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc"
when (isNothing date) (return ())
-- etc
-- make page object and send it to db
return ()
問題は、when
do ブロックを停止せず、他の部分の実行を妨げないことです。
これを実行する正しい方法は何ですか?
ベストアンサー1
return
Haskellでは他の言語と同じことをしませんreturn
。代わりに、return
モナド(この場合はIO
)に値を注入します。いくつかのオプションがあります。
最もシンプルなのはifを使うことです
scrapePage :: String -> IO ()
scrapePage url = do
doc <- fromUrl url
title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText
if (isNothing title) then return () else do
date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc"
if (isNothing date) then return () else do
-- etc
-- make page object and send it to db
return ()
もう一つの選択肢はunless
scrapePage url = do
doc <- fromUrl url
title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText
unless (isNothing title) do
date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc"
unless (isNothing date) do
-- etc
-- make page object and send it to db
return ()
ここでの一般的な問題は、IO
モナドが制御効果を持たないことです(例外を除く)。一方で、maybeモナドトランスフォーマーを使うこともできます。
scrapePage url = liftM (maybe () id) . runMaybeT $ do
doc <- liftIO $ fromUrl url
title <- liftIO $ liftM headMay $ runX $ doc >>> css "head.title" >>> getText
guard (isJust title)
date <- liftIO $ liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc"
guard (isJust date)
-- etc
-- make page object and send it to db
return ()
本格的なコントロール効果を得たいなら、ContT
scrapePage :: String -> IO ()
scrapePage url = runContT return $ do
doc <- fromUrl url
title <- liftM headMay $ runX $ doc >>> css "head.title" >>> getText
when (isNothing title) $ callCC ($ ())
date <- liftM headMay $ runX $ doc >>> css "span.dateTime" ! "data-utc"
when (isNothing date) $ callCC ($ ())
-- etc
-- make page object and send it to db
return ()
警告: 上記のコードはいずれもテストされておらず、型チェックも行われていません。