Haskell将do块转换为函数

3

我有一个工作正常的程序:

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE ScopedTypeVariables #-}

module GPS where

import Network.Wreq
import Control.Lens
import Text.HTML.TagSoup
import Data.ByteString.Lazy
import Data.ByteString.Lazy.Char8
import Data.Strings

getStr :: Tag ByteString -> String
getStr tag@(TagText str) = Data.ByteString.Lazy.Char8.unpack str
getStr _ = ""

filterStr :: [Tag ByteString] -> [String]
filterStr [] = []
filterStr (t:r) = [getStr t] ++ filterStr r

main :: IO ()
main = do
  r <- post "http://www.geocodeip.com" ["IP" := Data.ByteString.Lazy.Char8.pack "79.212.82.103"]
  html <- r ^. responseBody
  let tags = parseTags html
  let tr = Prelude.head $ sections (~== ("<tr>" :: String)) tags
  let tag = Prelude.filter isTagText tr
  let text = filterStr tag
  let ztext =Prelude.zip [0..] text
  let nlat = Prelude.head $ Prelude.map fst . Prelude.filter (\(_, str) -> strEq str ("Latitude:" :: String)) $ ztext
  let nlng = Prelude.head $ Prelude.map fst . Prelude.filter (\(_, str) -> strEq str ("Longitude:" :: String)) $ ztext
  let lat = read (Prelude.head $ Prelude.map snd . Prelude.filter (\(n, _) -> n == nlat + 1) $ ztext) :: Double
  let lng = read (Prelude.head $ Prelude.map snd . Prelude.filter (\(n, _) -> n == nlng + 1) $ ztext) :: Double
  print lat
  print lng

现在进入有趣的部分 - 我想将这个“do-Block”拆分成子函数,例如getHTML、getTags、makeText等...

因此我开始制作以下内容:

getHTML :: String -> IO ByteString
getHTML ip = fmap (view responseBody) (post "http://www.geocodeip.com" ["IP" := Data.ByteString.Lazy.Char8.pack ip])

这将替换我的do-Block的第一行和第二行,它应该获取HTML。这很好用,唯一的问题是它返回了一个带有IO类型的值,因此let tags = parseTags $ getHTML "79.212.82.103"无法工作:

[1 of 1] Compiling GPS              ( GPS.hs, interpreted )

GPS.hs:38:14:
    No instance for (Text.StringLike.StringLike (IO ByteString))
      arising from a use of ‘parseTags’
    In the expression: parseTags
    In the expression: parseTags $ getHTML "79.212.82.103"
    In an equation for ‘tags’:
        tags = parseTags $ getHTML "79.212.82.103"

GPS.hs:39:37:
    No instance for (Text.StringLike.StringLike (IO ByteString))
      arising from a use of ‘~==’
    In the first argument of ‘sections’, namely
      ‘(~== ("<tr>" :: String))’
    In the second argument of ‘($)’, namely
      ‘sections (~== ("<tr>" :: String)) tags’
    In the expression:
      Prelude.head $ sections (~== ("<tr>" :: String)) tags

GPS.hs:41:24:
    Couldn't match typeIO ByteString’ with ‘ByteString
    Expected type: [Tag ByteString]
      Actual type: [Tag (IO ByteString)]
    In the first argument of ‘filterStr’, namely ‘tag’
    In the expression: filterStr tag
Failed, modules loaded: none.

如何解决我的问题?

html <- getHTML "79.212.82.103" ; let tags = parseTags html你需要通过 <->>= 或其他方式将其绑定。 - Mephy
1个回答

4
现在这是一个单子动作,因此请使用单子语法:
tags <- parseTags <$> getHTML "79.212.82.103"
--   ⩓             ⩓
--   ╰─────────────│────────── bind instead of 'let'
--                 ╰─────────────────── functor application
<$>只是中缀fmap。您也可以在getHTML的实现中使用它。

非常感谢。这很有帮助,解释/设计得很好。谢谢。 - Martin Fischer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接