您正在寻找的是一个适应 JSON 解码器的函数:
decodeJson :: forall a. Json -> Either String a
使用F
而不是返回Either
。F
是 for 中定义的Data.Foreign
同义词Except MultipleErrors a
。为此,我们需要:
- 将我们的
String
错误转化为MultipleErrors
- 转换
Either
为Except
MultipleErrors
是 中定义的另一个同义词Data.Foreign
,这次是NonEmptyList ForeignError
. 看看ForeignError
还有一个构造函数,它也被称为ForeignError
让我们提供一些字符串消息。这让我们需要创建一个NonEmptyList
,这很容易:
remapError = pure <<< ForeignError
NonEmptyList
是Applicative
,所以我们可以用 来创建一个单元素列表pure
。
从Either
到到Except
也很简单。再次查看 Pursuit 中的定义,我们可以看到:
newtype ExceptT m e a = ExceptT (m (Either e a))
type Except = ExceptT Identity
所以ExceptT
这只是一个幻想Either
,给我们:
eitherToExcept = ExceptT <<< pure
这里pure
是抬Either e a
入m (Either e a)
,其中为Except
m ~ Identity
。
所以现在我们可以使用这些东西,并制作一个通用的“解码 JSON 以获取 Affjax 响应”功能:
decodeJsonResponse :: forall a. DecodeJson a => Json -> F a
decodeJsonResponse =
ExceptT <<< pure <<< lmap (pure <<< ForeignError) <<< decodeJson
这里唯一发生的另一件事是我们曾经lmap
映射到 , 的左侧部分Either
来执行错误消息类型转换位。
我们现在可以使用 Kleisli 组合 ( (<=<)
) 将其与将执行初始操作decodeJsonResponse
的原始链接在一起:fromResponse
ResponseContent -> F Json
instance respondableAnswer :: Respondable Answer where
responseType = Tuple (Just applicationJSON) JSONResponse
fromResponse = decodeJsonResponse <=< fromResponse
这是使用您的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