spo*_*ova 2 argonaut purescript
我正在尝试从Haskell服务器获取一些JSON数据,但我遇到了Respondeable实例,以及一般的Affjax.我已经使用Data.Argonaut.Generic.Aeson(GA)定义了EncodeJson + DecodeJson,但我无法弄清楚如何将其与Respondeable实例相匹配并且它来自响应函数.
它给了我错误"无法匹配类型外部与类型Json"但是可以重用我的decodeJson实例而不必手动创建任何其他东西吗?也许通过创建一个IsForeign实例,但使用GA.decodeJson?我只是不确定如何去做.我已经看到它是如何在https://github.com/purescript/purescript-foreign/blob/master/examples/Complex.purs手动完成的,但我有复杂的类型需要与我的Haskell JSON输出相匹配,手动完成这将是一个巨大的痛苦.
我正在使用purescript 10.7,Affjax 3.02和argonaut 2.0.0,以及argonaut-generic-codecs 5.1.0.谢谢!
testAffjax :: forall eff. Aff (ajax :: AJAX | eff) (Answer)
testAffjax = launchAff do
res <- affjax $ defaultRequest { url = "/", method = Left GET }
pure res.response
data Answer = Answer {
_answer :: String
, _isCorrect :: Boolean
, _hint :: String
}
{- PROBLEM -}
instance respondableAnswer :: Respondable Answer where
responseType = Tuple Nothing JSONResponse
fromResponse = GA.decodeJson {- Error here -}
derive instance genericAnswer :: Generic Answer
instance showAnswer :: Show Answer where
show = gShow
instance encodeAnswer :: EncodeJson Answer where
encodeJson = GA.encodeJson
instance decodeAnswer :: DecodeJson Answer where
decodeJson = GA.decodeJson
Run Code Online (Sandbox Code Playgroud)
您正在寻找的是一个适应JSON解码器的功能:
decodeJson :: forall a. Json -> Either String a
Run Code Online (Sandbox Code Playgroud)
返回使用F而不是Either.F是Data.Foreignfor中定义的同义词Except MultipleErrors a.要做到这一点,我们需要:
String错误翻译成一个MultipleErrorsEither为ExceptMultipleErrors是Data.Foreign这次定义的另一个同义词NonEmptyList ForeignError.看看ForeignError还有一个构造函数也ForeignError可以让我们提供一些字符串消息.这让我们需要创建一个NonEmptyList非常简单的东西:
remapError = pure <<< ForeignError
Run Code Online (Sandbox Code Playgroud)
NonEmptyList是的Applicative,所以我们可以创建一个单元素列表pure.
从去Either到Except也很简单.再看看Pursuit中的定义,我们可以看到:
newtype ExceptT m e a = ExceptT (m (Either e a))
type Except = ExceptT Identity
Run Code Online (Sandbox Code Playgroud)
所以ExceptT只是一种幻想Either,给我们:
eitherToExcept = ExceptT <<< pure
Run Code Online (Sandbox Code Playgroud)
在pure这里还是要提升Either e a到m (Either e a),这为Except m ~ Identity.
所以现在我们可以采用这些东西,并制作一个通用的"为Affjax响应解码JSON"功能:
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson
Run Code Online (Sandbox Code Playgroud)
在这里发生的唯一另一件事是我们用来lmap映射左边的部分Either,来做错误消息类型转换位.
我们现在可以使用Kleisli组成((<=<)this)来链decodeJsonResponse加上原有的fromResponse,将做初步ResponseContent -> F Json:
instance respondableAnswer :: Respondable Answer where
responseType = Tuple (Just applicationJSON) JSONResponse
fromResponse = decodeJsonResponse <=< fromResponse
Run Code Online (Sandbox Code Playgroud)
以下是使用您的Answer类型的完整示例:
module Main where
import Prelude
import Control.Monad.Aff (Aff)
import Control.Monad.Except (ExceptT(..))
import Data.Argonaut (class DecodeJson, class EncodeJson, Json, decodeJson)
import Data.Argonaut.Generic.Argonaut as GA
import Data.Bifunctor (lmap)
import Data.Foreign (F, ForeignError(..))
import Data.Generic (class Generic, gShow)
import Data.Maybe (Maybe(..))
import Data.MediaType.Common as MediaType
import Data.Tuple (Tuple(..))
import Network.HTTP.Affjax as AX
import Network.HTTP.Affjax.Response as AXR
testAffjax :: forall eff. Aff (ajax :: AX.AJAX | eff) Answer
testAffjax = _.response <$> AX.get "/"
newtype Answer = Answer
{ _answer :: String
, _isCorrect :: Boolean
, _hint :: String
}
derive instance genericAnswer :: Generic Answer
instance showAnswer :: Show Answer where
show = gShow
instance encodeAnswer :: EncodeJson Answer where
encodeJson = GA.encodeJson
instance decodeAnswer :: DecodeJson Answer where
decodeJson = GA.decodeJson
instance respondableAnswer :: AXR.Respondable Answer where
responseType = Tuple (Just MediaType.applicationJSON) AXR.JSONResponse
fromResponse = decodeJsonResponse <=< AXR.fromResponse
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson
Run Code Online (Sandbox Code Playgroud)