我编写了一个实用程序类型和函数,旨在帮助解析某些行多态类型(在我的情况下,特别是任何扩展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}
然而,当我尝试使用它时,它会导致代码出现这种类型的错误(在我较大的代码库中,在我实现readIdTypePair
函数之前,一切都很好(:
No type class instance was found for
Prim.RowList.RowToList ( identifier :: Foreign
, identifierType :: Foreign
| t3
)
t4
The instance head contains unknown type variables. Consider adding a type annotation.
while applying a function readJSON'
of type ReadForeign t2 => String -> ExceptT (NonEmptyList ForeignError) Identity t2
to argument jsStr
while checking that expression readJSON' jsStr
has type t0 t1
in value declaration readRecordJSON
where t0 is an unknown type
t1 is an unknown type
t2 is an unknown type
t3 is an unknown type
t4 is an unknown type
我有一个生动的要点来说明我的问题。
但是,对于子孙后代来说,这里有一个完整的例子:
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 }
您的问题是recBase
不一定是Resource
类型。
编译器有两个参考点来确定recBase
的类型:(1(recBase.identifiers
与readIdTypePair
一起使用的事实和(2(readRecordJSON
的返回类型。
从第一点开始,编译器可以得出结论:
recBase :: { identifiers :: Array (Record (IdTypePair r)) | p }
对于一些未知的CCD_ 9和CCD_。它(至少(有一个名为identifiers
的字段,这一事实来自点语法,该字段的类型来自readIdTypePair
的参数以及idents
是Array
的事实。但除了identifiers
(用p
表示(之外,可能还有更多的字段,并且identifiers
的每个元素都是部分记录(用r
表示(。
从第二点来看,编译器可以得出结论:
recBase :: { identifiers :: a }
等等,什么?为什么a
而不是Array Identifier
?Resource
的定义没有明确规定identifiers :: Array Identifier
吗?
是的,确实如此,但诀窍是:recBase
的类型不一定是Resource
。readRecordJSON
的返回类型是Resource
,但在recBase
和readRecordJSON
的返回类型之间有一个记录更新操作recBase { identifiers = idents }
,可以更改字段的类型。
是的,PureScript中的记录更新是变形的。看看这个:
> 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
另外,我觉得有必要评论一下你指定记录的奇怪方式:你首先声明一行,然后用它做一个记录。仅供参考,它可以直接用大括号完成,例如:
type Resource = {
identifiers :: Array Identifier
}
如果你这样做是出于审美原因,我没有反对意见。但如果你不知道-现在你知道了:-(