Purescript重用Argonaut JSON解码为Affjax响应

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)

gb.*_*gb. 7

您正在寻找的是一个适应JSON解码器的功能:

decodeJson :: forall a. Json -> Either String a
Run Code Online (Sandbox Code Playgroud)

返回使用F而不是Either.FData.Foreignfor中定义的同义词Except MultipleErrors a.要做到这一点,我们需要:

  1. 将我们的String错误翻译成一个MultipleErrors
  2. 转换EitherExcept

MultipleErrorsData.Foreign这次定义的另一个同义词NonEmptyList ForeignError.看看ForeignError还有一个构造函数也ForeignError可以让我们提供一些字符串消息.这让我们需要创建一个NonEmptyList非常简单的东西:

remapError = pure <<< ForeignError
Run Code Online (Sandbox Code Playgroud)

NonEmptyList是的Applicative,所以我们可以创建一个单元素列表pure.

从去EitherExcept也很简单.再看看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 am (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)