Справка по Haskell Curl - PullRequest
       13

Справка по Haskell Curl

5 голосов
/ 16 ноября 2010

Хорошо, я пытаюсь обернуть голову вокруг ввода-вывода в Haskell, и я решил написать короткое небольшое приложение для работы с веб-страницами. Фрагмент, с которым я сталкиваюсь (извинения перед bobince , хотя, если честно, я не пытаюсь разобрать HTML здесь, просто извлечь одно или два значения) :

titleFromUrl url = do
    (_, page) <- curlGetString url [CurlTimeout 60]   
    matchRegex (mkRegexWithOpts "<title>(.*?)</title>" False True) page

Вышеприведенный код должен принимать URL в виде строки, сканировать страницу, на которую он указывает, с помощью matchRegex и возвращать либо Nothing, либо Just [a], где a - совпавшая (возможно, многострочная) строка. Расстраивает то, что когда я пытаюсь сделать

Prelude> (_, page) <- curlGetString url [CurlTimeout 60]
Prelude> matchRegex (mkRegexWithOpts "<title>(.*?)</title>" False True) page

в интерпретаторе, он делает именно то, что я хочу. Когда я пытаюсь загрузить то же самое выражение и связанный imports из файла, это выдает мне ошибку вывода типа, утверждающую, что оно couldn't match expected type 'IO b' against inferred type 'Maybe [String]'. Это говорит мне, что я упускаю что-то маленькое и фундаментальное, но я не могу понять, что. Я пытался явным образом привести page к строке, но это просто программирование с помощью суеверия (и это не сработало ни в коем случае).

Есть какие-нибудь подсказки?

1 Ответ

8 голосов
/ 16 ноября 2010

Да, GHCi принимает любые значения. Вы можете сказать:

ghci> 4
4
ghci> print 4
4

Но эти два значения (4 и print 4) явно не равны. Волшебство GHC заключается в том, что если то, что вы ввели, оценивается как IO something, то оно выполняет это действие (и печатает результат, если something не ()). Если это не так, то он вызывает show для значения и печатает это. В любом случае, эта магия недоступна из вашей программы.

Когда вы говорите:

do foo <- bar :: IO Int
   baz

baz ожидается типа IO something, в противном случае это ошибка типа. Это позволит вам выполнить ввод-вывод, а затем вернуть чистое значение. Вы можете проверить это, заметив, что отладка вышеприведенного приводит к:

bar >>= (\foo -> baz)

И

-- (specializing to IO for simplicity)
(>>=) :: IO a -> (a -> IO b) -> IO b

Поэтому

bar :: IO a
foo :: a
baz :: IO b

Способ исправить это - преобразовать возвращаемое значение в значение IO с помощью функции return:

return :: a -> IO a  -- (again specialized to IO)

Тогда ваш код:

titleFromUrl url = do
    (_, page) <- curlGetString url [CurlTimeout 60]   
    return $ matchRegex (mkRegexWithOpts "<title>(.*?)</title>" False True) page

Для большей части приведенного выше обсуждения вы можете заменить любую монаду на IO (например, Maybe, [], ...), и она все равно будет верной.

...