如何在 PureScript 中为 SimpleJSON 创建自定义解析器 Monad?

How to create a custom parser Monad for SimpleJSON in PureScript?

我有以下内容,直到我尝试定义 readJSON':

newtype JSONWithErr a = JSONWithErr (Writer (Array Foreign.ForeignError) a)

derive newtype instance jsonWithErrApply :: Apply JSONWithErr
derive newtype instance jsonWithErrApplicative :: Applicative JSONWithErr
derive newtype instance jsonWithErrFunctor :: Functor JSONWithErr
derive newtype instance jsonWithErrBind :: Bind JSONWithErr
derive newtype instance jsonWithErrMonad :: Monad JSONWithErr
derive newtype instance jsonWithErrTell :: MonadTell (Array Foreign.ForeignError) JSONWithErr
derive newtype instance jsonWithErrWriter :: MonadWriter (Array Foreign.ForeignError) JSONWithErr

newtype JSONParse a = JSONParse (ExceptT (NonEmptyList ForeignError) JSONWithErr a)

derive newtype instance jsonParseApply :: Apply JSONParse
derive newtype instance jsonParseApplicative :: Applicative JSONParse
derive newtype instance jsonParseFunctor :: Functor JSONParse
derive newtype instance jsonParseBind :: Bind JSONParse
derive newtype instance jsonParseMonad :: Monad JSONParse
derive newtype instance jsonParseTell :: MonadTell (Array Foreign.ForeignError) JSONParse
derive newtype instance jsonParseWriter :: MonadWriter (Array Foreign.ForeignError) JSONParse
derive newtype instance jsonParseThrow :: MonadThrow (NonEmptyList ForeignError) JSONParse

generalize :: forall m a. Monad m => Identity a -> m a
generalize = unwrap >>> pure

-- type Except e = ExceptT e Identity
genExcept :: forall m e a. Monad m => ExceptT e Identity a -> ExceptT e m a
genExcept = unwrap >>> generalize >>> ExceptT

readJSON' :: forall a. JSON.ReadForeign a => String -> JSONParse a
readJSON' s = JSONParse $ genExcept $ (pure >>> JSONWithErr) <$> (JSON.readJSON' s) -}

这里的错误,涵盖了 readJSON' 的整个定义,是:

Could not match type

    JSONWithErr t2

  with type

    a0


while trying to match type JSONParse t1
  with type JSONParse a0
while checking that expression (apply JSONParse) ((apply genExcept) ((map (...)) (readJSON' s)))
  has type JSONParse a0
in value declaration readJSON'

where a0 is a rigid type variable
        bound at (line 0, column 0 - line 0, column 0)
      t1 is an unknown type
      t2 is an unknown type

我试图稍微简化一下,但我似乎 运行 遇到了约束问题;在以下简化中,键入的孔由于早期错误而不起作用:

tmp :: forall a. JSON.ReadForeign a => String -> ExceptT (NonEmptyList ForeignError) JSONWithErr a
tmp s = ?help (JSON.readJSON' s)

错误:

  No type class instance was found for

    Simple.JSON.ReadForeign t0

  The instance head contains unknown type variables. Consider adding a type annotation.

while applying a function readJSON'
  of type ReadForeign t0 => String -> ExceptT (NonEmptyList ForeignError) Identity t0
  to argument s
while inferring the type of readJSON' s
in value declaration tmp

我觉得你只是被自己的小聪明给缠住了。

看看 genExcept 期望的参数类型:ExceptT e Identity a,但您也可以看出 e ~ (NonEmptyList ForeignError),因为 genExcept 的结果稍后会被包装在 JSONParse

因此 genExcept 期望的参数类型,如在 readJSON' 的主体中实例化的那样,是 ExceptT (NonEmptyList ForeignError) Identity a,为此有一个方便的类型别名 - F.

所以我们可以判断它一定是:

(pure >>> JSONWithErr) <$> (JSON.readJSON' s) :: F a

但是look at the return type of JSON.readJSON':

readJSON' :: forall a. ReadForeign a => String -> F a

所以 JSON.readJSON' s 已经是类型 F a

然后你将它包装在一些 pure 中,然后在 JSONWithErr 中,所以整个表达式变成:

(pure >>> JSONWithErr) <$> (JSON.readJSON' s) :: F (JSONWithErr (b a))

某些 bpure 决定。

这不是 genExcept 所期望的。所以很自然地你会得到一个错误。

根据我对您最终意图的假设,您似乎可以将 JSON.readJSON' s 直接传递给 genExcept,然后类型将得到解决:

readJSON' :: forall a. JSON.ReadForeign a => String -> JSONParse a
readJSON' s = JSONParse $ genExcept $ JSON.readJSON' s