如何在PureScript中使用SimpleJSON解析行多态记录?
我编写了一个实用程序类型和函数,用于帮助解析某些行多态类型(在我的例子中,特别是扩展如何在PureScript中使用SimpleJSON解析行多态记录?,json,purescript,row-polymorphism,Json,Purescript,Row Polymorphism,我编写了一个实用程序类型和函数,用于帮助解析某些行多态类型(在我的例子中,特别是扩展BaseIdRows: type IdTypePairF r = (identifier :: Foreign, identifierType :: Foreign | r) readIdTypePair :: forall r. Record (IdTypePairF r) -> F Identifier readIdTypePair idPairF = do id <- readNEStr
BaseIdRows
:
type IdTypePairF r = (identifier :: Foreign, identifierType :: Foreign | r)
readIdTypePair :: forall r. Record (IdTypePairF r) -> F Identifier
readIdTypePair idPairF = do
id <- readNEStringImpl idPairF.identifier
idType <- readNEStringImpl idPairF.identifierType
pure $ {identifier: id, identifierType: idType}
我有一个可以证明我的问题的例子
但是,对于后代来说,这是一个完整的例子:
module Main where
import Control.Monad.Except (except, runExcept)
import Data.Array.NonEmpty (NonEmptyArray, fromArray)
import Data.Either (Either(..))
import Data.HeytingAlgebra ((&&), (||))
import Data.Lazy (Lazy, force)
import Data.Maybe (Maybe(..))
import Data.Semigroup ((<>))
import Data.String.NonEmpty (NonEmptyString, fromString)
import Data.Traversable (traverse)
import Effect (Effect(..))
import Foreign (F, Foreign, isNull, isUndefined)
import Foreign as Foreign
import Prelude (Unit, bind, pure, ($), (>>=), unit)
import Simple.JSON as JSON
main :: Effect Unit
main = pure unit
type ResourceRows = (
identifiers :: Array Identifier
)
type Resource = Record ResourceRows
type BaseIdRows r = (
identifier :: NonEmptyString
, identifierType :: NonEmptyString
| r
)
type Identifier = Record (BaseIdRows())
-- Utility type for parsing
type IdTypePairF r = (identifier :: Foreign, identifierType :: Foreign | r)
readNEStringImpl :: Foreign -> F NonEmptyString
readNEStringImpl f = do
str :: String <- JSON.readImpl f
except $ case fromString str of
Just nes -> Right nes
Nothing -> Left $ pure $ Foreign.ForeignError
"Nonempty string expected."
readIdTypePair :: forall r. Record (IdTypePairF r) -> F Identifier
readIdTypePair idPairF = do
id <- readNEStringImpl idPairF.identifier
idType <- readNEStringImpl idPairF.identifierType
pure $ {identifier: id, identifierType: idType}
readRecordJSON :: String -> Either Foreign.MultipleErrors Resource
readRecordJSON jsStr = runExcept do
recBase <- JSON.readJSON' jsStr
--foo :: String <- recBase.identifiers -- Just comment to check inferred type
idents :: Array Identifier <- traverse readIdTypePair recBase.identifiers
pure $ recBase { identifiers = idents }
modulemain其中
import Control.Monad.Except(Except,runExcept)
导入Data.Array.NonEmpty(NonEmptyArray,fromArray)
导入数据。或者(或者(…)
导入Data.HeytingAlgebra(&&),(| |)
导入数据。惰性(惰性,强制)
导入数据。可能(可能(…)
导入数据。半群(())
导入Data.String.NonEmpty(NonEmptyString,fromString)
导入数据。可遍历(遍历)
进口影响(影响(…)
导入外部(F、外部、isNull、isUndefined)
进口外国商品
导入前奏曲(单位,绑定,纯,($),(>>=),单位)
将Simple.JSON导入为JSON
主要影响单位
主=纯单位
类型ResourceRows=(
标识符::数组标识符
)
类型资源=记录资源行
类型BaseIdRows r=(
标识符::非空字符串
,identifierType::非空字符串
|r
)
类型标识符=记录(BaseIdRows())
--用于解析的实用程序类型
类型IdTypePairF r=(标识符::外部,标识符类型::外部| r)
readNEStringImpl::Foreign->F NonEmptyString
readNEStringImpl f=do
str::字符串右键
Nothing->Left$pure$Foreign.ForeignError
“应为非空字符串。”
readIdTypePair::所有r.记录(IdTypePairF r)->F标识符
readIdTypePair IDPAIR=do
id您的问题是,recBase
不一定属于Resource
类型
编译器有两个确定recBase
类型的参考点:(1)事实上recBase.identifiers
与readIdTypePair
一起使用;(2)返回类型readRecordJSON
从第一点开始,编译器可以得出以下结论:
recBase :: { identifiers :: Array (Record (IdTypePair r)) | p }
recBase :: { identifiers :: a }
对于一些未知的r
和p
。事实是它(至少)名为identifiers
的字段来自点语法,该字段的类型来自readIdTypePair
的参数,再加上idents
是数组的事实。但是除了identifiers
(由p
表示)之外,还可能有更多的字段,并且标识符
的每个元素都是部分记录(由r
表示)
从第二点出发,编译器可以得出以下结论:
recBase :: { identifiers :: Array (Record (IdTypePair r)) | p }
recBase :: { identifiers :: a }
等等,什么?为什么a
而不是Array Identifier
?资源的定义没有明确指定标识符::Array Identifier
是的,确实如此,但这里有个诀窍:recBase
的类型不必是Resource
。readRecordJSON
的返回类型是Resource
,但是在recBase
和readRecordJSON
的返回类型之间有一个记录更新操作recBase{identifiers=identifiers}
,它可以更改字段的类型
是的,PureScript中的记录更新是plymorphic的。请查看以下内容:
> x = { a: 42 }
> y = x { a = "foo" }
> y
{ a: "foo" }
查看x.a
的类型是如何更改的?此处x:{a::Int}
,但y:{a::String}
代码中也是如此:recBase.identifiers::Array(IdTypePairF r)
用于一些未知的r
,但是(recBase{identifiers=idents})。identifiers::Array Identifier
满足readRecordJSON
的返回类型,但行r
仍然未知
要修复此问题,您有两个选项。选项1-制作readIdTypePair
完整记录,而不是部分记录:
readIdTypePair :: Record (IdTypePairF ()) -> F Identifier
选项2-明确指定recBase
的类型:
recBase :: { identifiers :: Array (Record (IdTypePairF ())) } <- JSON.readJSON' jsStr
如果你这样做是为了美观,我没有异议。但是如果你不知道-现在你知道了:-)你的问题是recBase
不一定属于资源类型
编译器有两个确定recBase
类型的参考点:(1)事实上recBase.identifiers
与readIdTypePair
一起使用;(2)返回类型readRecordJSON
从第一点开始,编译器可以得出以下结论:
recBase :: { identifiers :: Array (Record (IdTypePair r)) | p }
recBase :: { identifiers :: a }
对于一些未知的r
和p
。它(至少)有一个名为identifiers
的字段这一事实来自点语法,该字段的类型来自readIdTypePair
的参数以及idents
是数组的事实。但是除了标识符
(由p
表示)之外,可能还有更多的字段,标识符
的每个元素都是一个部分记录(由r
表示)
从第二点出发,编译器可以得出以下结论:
recBase :: { identifiers :: Array (Record (IdTypePair r)) | p }
recBase :: { identifiers :: a }
等等,什么?为什么a
而不是数组标识符
?Resource
的定义是否明确规定了identifiers::Array Identifier
是的,确实如此,但这里有个诀窍:recBase
的类型不必是Resource
。readRecordJSON
的返回类型是Resource
,但是在recBase
和readRecordJSON
的返回类型之间有一个记录更新操作recBase{identifiers=idents}
,它可以更改字段的类型
是的,PureScript中的记录更新是plymorphic。看看这个:
> x = { a: 42 }
> y = x { a = "foo" }
> y
{ a: "foo" }
查看x.a
的类型如何更改?这里是x::{a::Int}
,但是y:{a::String}
代码中也是如此:recBase.identifiers::Array(IdTypePairF r)
用于一些未知的r
,但是(recBase{identifiers=idents})。identifiers::Array Identifier
再