Подтвердить что ты не робот

Как мне сделать возврат блока раньше?

Я пытаюсь очистить веб-страницу с помощью Haskell и скомпилировать результаты в объект.

Если по какой-то причине я не могу получить все элементы со страниц, я хочу перестать пытаться обработать страницу и вернуться раньше.

Например:

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 или не выполняет другие части.

Каков правильный способ сделать это?

4b9b3361

Ответ 1

return в haskell не делает то же самое, что и return на других языках. Вместо этого, что return заключается в том, чтобы ввести значение в монаду (в данном случае IO). У вас есть пара опций

наиболее простым является использование, если

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 не имеет управляющих эффектов (кроме исключений). С другой стороны, вы можете использовать трансформатор, возможно, монада

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 ()

ПРЕДУПРЕЖДЕНИЕ: ни один из вышеуказанных кодов не был протестирован или даже не был проверен!

Ответ 2

Используйте трансформатор монады!

import Control.Monad.Trans.Class -- from transformers package
import Control.Error.Util        -- from errors package

scrapePage :: String -> IO ()
scrapePage url = maybeT (return ()) return $ do
  doc <- lift $ fromUrl url
  title <- liftM headMay $ lift . runX $ doc >>> css "head.title" >>> getText
  guard . not $ isNothing title
  date <- liftM headMay $ lift . runX $ doc >>> css "span.dateTime" ! "data-utc"
  guard . not $ isNothing date
  -- etc
  -- make page object and send it to db
  return ()

Для большей гибкости возвращаемого значения при раннем возврате используйте throwError/eitherT/eitherT вместо mzero/maybeT/maybeT. (Хотя тогда вы не можете использовать guard.)

(Возможно, также используйте headZ вместо headMay и выровняйте явный guard.)

Ответ 3

Я никогда не работал с Haskell, но кажется, что это легко. Попробуйте when (isNothing date) $ exit (). Если это также не работает, убедитесь, что ваше утверждение верное. Также см. Этот сайт для получения дополнительной информации: Отключение от цикла.