我想简化此代码
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Network.HTTP.Types
import Data.Text
getJSON :: String -> IO (Either String Value)
getJSON url = eitherDecode <$> simpleHttp url
--------------------------------------------------------------------
maybeJson <- getJSON "abc.com"
case maybeJson of
Right jsonValue -> case jsonValue of
(Object jsonObject) ->
case (HashMap.lookup "key123" jsonObject) of
(Just (String val)) -> Data.Text.IO.putStrLn val
_ -> error "Couldn't get the key"
_ -> error "Unexpected JSON"
Left errorMsg -> error $ "Error in parsing: " ++ errorMsg
使用Monad的do语法
maybeJson <- getJSON "abc.com/123"
let toPrint = do
Right jsonValue <- maybeJson
Object jsonObject <- jsonValue
Just (String val) <- HashMap.lookup "key123" jsonObject
return val
case toPrint of
Just a -> Data.Text.IO.putStrLn a
_ -> error "Unexpected JSON"
它给了我3个错误:
src/Main.hs:86:19:
Couldn't match expected type `Value'
with actual type `Either t0 (Either String Value)'
In the pattern: Right jsonValue
In a stmt of a 'do' block: Right jsonValue <- maybeJson
src/Main.hs:88:19:
Couldn't match expected type `Value' with actual type `Maybe Value'
In the pattern: Just (String val)
In a stmt of a 'do' block:
Just (String val) <- HashMap.lookup "key123" jsonObject
src/Main.hs:88:40:
Couldn't match type `Maybe' with `Either String'
Expected type: Either String Value
Actual type: Maybe Value
Even when I replace
Just (String val) <- HashMap.lookup "key123" jsonObject
带
String val <- HashMap.lookup "key123" jsonObject
我收到了关于Either的另一个类似错误:
Couldn't match type `Maybe' with `Either String'
Expected type: Either String Value
Actual type: Maybe Value
In the return type of a call of `HashMap.lookup'
In a stmt of a 'do' block:
String val <- HashMap.lookup "key123" jsonObject
如何解决这些错误?
答案 0 :(得分:2)
您无法轻松将其简化为单个记号块,因为每个case
都匹配不同的类型。第一个是解包either
,第二个是Value
,第三个是Maybe
。表示法通过一种类型将所有内容组合在一起起作用,因此它不能直接应用于此。
你可以转换所有情况以使用相同的monad,然后在do-block中将其全部写出来。例如,您可以使用辅助函数来执行第二个和第三个模式匹配,并生成适当的Either
。但是,这与你现在所拥有的不同!
事实上,如果我采用这种方法,我只是满足于将两个内部匹配提取到他们自己的where
变量中并保留它。试图将整个事物整合成一个monad只是混淆了这个问题;它不是正确的抽象。
相反,您可以实现不同类型的抽象。特别是,考虑使用具有棱镜的lens
库来处理嵌套模式匹配。它甚至支持aeson!你想要的功能看起来像这样:
decode :: String -> Maybe Value
decode json = json ^? key "key123"
您还可以将其与更具体的棱镜结合使用,例如,如果您需要字符串值:
decode :: String -> Maybe String
decode json = json ^? key "key123" . _String
这需要解析json,确保它是一个对象并获取指定键的任何内容。唯一的问题是它没有给你一个关于失败原因的有用错误信息;不幸的是,我对镜片不够了解如何解决这个问题(如果可能的话)。
答案 1 :(得分:1)
由于您在IO monad中,所有<-
都将假设您正在处理IO操作。当你写
do
Right jsonValue <- maybeJson
Object jsonObject <- jsonValue
你是说jsonValue必须是一个IO动作,就像maybeJson一样。但这种情况并非如此! jsonValue只是一个常规的Either
值。这里的说法是使用do-block let
而不是<-
:
do
Right jsonValue <- maybeJson
let Object jsonObject = jsonValue
但是,重要的是要注意,在两个版本的代码中,如果JSON解析失败,则使用不可恢复的error
来中止程序。如果你想能够收集错误,基本的想法是将你的值转换为Either(然后使用monad实例为Either以避免有大量嵌套的case表达式)
答案 2 :(得分:1)
因此,Monad的do表达式中的每一行都必须返回该Monadic类型的值。 Monad是一个类型类,而不是一个类型。所以把所有东西都放在一个Monad中并不是一个明智的陈述。
你可以尝试使用Maybe monad中的所有内容编写代码。 假设您已经获取了JSON值:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Network.HTTP
import qualified Data.Map as M
import Control.Applicative
import qualified Data.HashMap.Strict as HM
--------------------------------------------------------------------
main = do
maybeJson <- return $ toJSON (M.fromList [("key123","value")] :: M.Map String String)
ioVal <- return $ do -- The Maybe monad do expression starts here
maybeJson <- Just maybeJson
jsonObject <- case maybeJson of
Object x -> Just x
_ -> Nothing
val <- HM.lookup "key123" jsonObject
return val
putStrLn $ show ioVal
一旦我们开始在Maybe monad中工作,每个表达式都必须返回Maybe Something
值。 Maybe
monad的工作方式是Just something
的任何内容都是纯粹的something
,你可以使用它,但如果你得到Nothing
,那么其余的Nothing
将跳过代码,您将获得import qualified Network.HTTP as H
main = do
postData <- return $ H.urlEncodeVars [("someVariable","someValue")]
request <- return $ H.postRequestWithBody "http://www.google.com/recaptcha/api/verify" "application/x-www-form-urlencoded" postData
putStrLn $ show request
-- Make the request
(Right response) <- H.simpleHTTP request
-- Print status code
putStrLn $ show $ H.rspCode response
-- Print response
putSrLn $ show $ H.rspBody response
。
坠落的这个属性对于Maybe monad来说是独一无二的。不同的monad表现不同。
您应该在此处阅读有关Monads和IO monad的更多信息:http://www.haskell.org/haskellwiki/Introduction_to_IO
你应该阅读更多关于monad的信息以及它们真正帮助你做的事情: http://learnyouahaskell.com/a-fistful-of-monads (你应该完成前面的章节,然后进入本章。一旦你这样做,你就会对发生的事情有一个非常了解的。)
我还认为你的HTTP请求搞砸了。以下是您可以使用的POST请求示例。
import qualified Data.ByteString.Lazy as LC
import qualified Data.ByteString.Char8 as C
import qualified Data.Aeson as DA
responseBody <- return $ H.rspBody response
responseJSON <- return (DA.decode (LC.fromChunks [C.pack responseBody]) :: Maybe DA.Value)
更新: 使用以下内容可帮助您获取JSON值:
{{1}}
您必须创建一个请求对象才能发出请求。有很多帮手。我的帖子请求是最常见的情况: http://hackage.haskell.org/package/HTTP-4000.0.5/docs/Network-HTTP.html