gpt4 book ai didi

haskell - IO 类型的链接函数(可能是 a )

转载 作者:行者123 更新时间:2023-12-02 14:02:06 25 4
gpt4 key购买 nike

我正在编写一个小型库,用于与一些外部 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com