- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个小型库,用于与一些外部 API 进行交互。一组函数将构造对 yahoo api 的有效请求并将结果解析为数据类型。另一组函数将根据 IP 查找用户当前位置,并返回表示当前位置的数据类型。虽然代码可以工作,但似乎必须显式地进行模式匹配以对 IO 类型的多个函数进行排序(也许是 a)。
-- Yahoo API
constructQuery :: T.Text -> T.Text -> T.Text
constructQuery city state = "select astronomy, item.condition from weather.forecast" <>
" where woeid in (select woeid from geo.places(1)" <>
" where text=\"" <> city <> "," <> state <> "\")"
buildRequest :: T.Text -> IO ByteString
buildRequest yql = do
let root = "https://query.yahooapis.com/v1/public/yql"
datatable = "store://datatables.org/alltableswithkeys"
opts = defaults & param "q" .~ [yql]
& param "env" .~ [datatable]
& param "format" .~ ["json"]
r <- getWith opts root
return $ r ^. responseBody
run :: T.Text -> IO (Maybe Weather)
run yql = buildRequest yql >>= (\r -> return $ decode r :: IO (Maybe Weather))
-- IP Lookup
getLocation:: IO (Maybe IpResponse)
getLocation = do
r <- get "http://ipinfo.io/json"
let body = r ^. responseBody
return (decode body :: Maybe IpResponse)
-- 组合器
runMyLocation:: IO (Maybe Weather)
runMyLocation = do
r <- getLocation
case r of
Just ip -> getWeather ip
_ -> return Nothing
where getWeather = (run . (uncurry constructQuery) . (city &&& region))
是否可以将 getLocation 线程化并一起运行,而无需借助显式模式匹配来“摆脱” Maybe Monad?
最佳答案
可以愉快地嵌套了do
对应于不同 monad 的 block ,因此拥有 Maybe Weather
类型的 block 就可以了。在你的IO (Maybe Weather)
中间 block 。
例如,
runMyLocation :: IO (Maybe Weather)
runMyLocation = do
r <- getLocation
return $ do ip <- r; return (getWeather ip)
where
getWeather = run . (uncurry constructQuery) . (city &&& region)
这个简单的模式do a <- r; return f a
表示您不需要 Maybe
的 monad 实例无论如何 - 一个简单的fmap
就够了
runMyLocation :: IO (Maybe Weather)
runMyLocation = do
r <- getLocation
return (fmap getWeather r)
where
getWeather = run . (uncurry constructQuery) . (city &&& region)
现在您看到相同的模式再次出现,因此您可以编写
runMyLocation :: IO (Maybe Weather)
runMyLocation = fmap (fmap getWeather) getLocation
where
getWeather = run . (uncurry constructQuery) . (city &&& region)
其中外fmap
正在映射您的IO
行动、内心fmap
正在映射您的Maybe
值。
我误解了 getWeather
的类型(见下面的评论)这样你最终会得到 IO (Maybe (IO (Maybe Weather)))
而不是IO (Maybe Weather)
.
您需要的是通过两层 monad 堆栈的“连接”。这本质上是 monad 转换器为您提供的(请参阅 @dfeuer 的答案),但在 Maybe
的情况下可以手动编写此组合器。 -
import Data.Maybe (maybe)
flatten :: (Monad m) => m (Maybe (m (Maybe a))) -> m (Maybe a)
flatten m = m >>= fromMaybe (return Nothing)
在这种情况下你可以写
runMyLocation :: IO (Maybe Weather)
runMyLocation = flatten $ fmap (fmap getWeather) getLocation
where
getWeather = run . (uncurry constructQuery) . (city &&& region)
应该有正确的类型。如果您要像这样链接多个函数,则需要多次调用 flatten
,在这种情况下,构建一个 monad 转换器堆栈可能会更容易(在 @dfeuer 的答案中有警告)。
我在 Transformer 或 mtl 库中称为“flatten”的函数可能有一个规范名称,但我目前找不到它。
请注意函数 fromMaybe
来自Data.Maybe
本质上是为您进行案例分析,但将其抽象为函数。
关于haskell - IO 类型的链接函数(可能是 a ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32442173/
我是一名优秀的程序员,十分优秀!