9阅网

您现在的位置是:首页 > 知识 > 正文

知识

json - Aeson解码成通用类型的类型检查有问题。

admin2022-11-03知识15

这是我第一次尝试使用Aeson进行JSON反序列化。我在为我所有的域数据类型检查通用解码函数时遇到了麻烦,尽管为单一的具体类型提供相应的解码函数是可行的。

这里是多态函数。

import qualified RIO.ByteString.Lazy           as BL
import qualified Data.Aeson                    as J
import qualified Path.Posix                    as P

loadDomainData :: J.FromJSON dData => FC.AbsFilePath -> IO dData
loadDomainData filePath = do
    fileContents <- readFileBinary $ P.toFilePath filePath
    let
        decData :: Maybe dData
        decData = J.decode $ BL.fromStrict fileContents
    case decData of
        Just d -> return d
        Nothing -> throwString ("Could not decode data file " <> P.toFilePath filePath)

在最初的失败后,我为解码器的目标类型插入了一个类型注释,但没有用。如果我尝试编译它,结果出现了以下类型检查错误。

    • Could not deduce (J.FromJSON dData1)
        arising from a use of ‘J.decode’
      from the context: J.FromJSON dData
        bound by the type signature for:
                   loadDomainData :: forall dData.
                                     J.FromJSON dData =>
                                     FC.AbsFilePath -> IO dData
        at src/Persistence/File/ParticipantRepository.hs:44:1-64
      Possible fix:
        add (J.FromJSON dData1) to the context of
          the type signature for:
            decData :: forall dData1. Maybe dData1
    • In the expression: J.decode $ BL.fromStrict fileContents
[..]

我到底漏了什么?谢谢你的任何见解



【回答】:

你根本不应该需要类型注释。 没有它是不是就不能正常编译了?

你缺少的是,类型签名中的变量在一个 letwhere 子句不在包含函数的类型签名范围内。 所以,类型变量 dData 签名中 loadDomainData 是完全没有关系的 dData 签名中 decData. GHC 抱怨说,GHC 中的类型 decData 没有 J.FromJSON 实例,因为类型签名说它没有。 你可以添加它。

decData :: J.FromJSON dData => Maybe dData

或者你可以打开 ScopedTypeVariables 扩展,并修改包含函数的类型签名,以标记为 dData 变量为scoped。

loadDomainData :: forall dData. J.FromJSON dData => FilePath -> IO dData

而保持相同的 decData 声明如前 forall 而不 constraint):

decData :: Maybe dData

或者,如上所述,您可以删除 decData 完全可以。 所以,以下三种情况应该都可以。

{-# LANGUAGE ScopedTypeVariables #-}

-- Add constraint to `decData` signature
loadDomainData :: J.FromJSON dData => FC.AbsFilePath -> IO dData
loadDomainData filePath = do
    fileContents <- readFileBinary $ P.toFilePath filePath
    let
        decData :: J.FromJSON dData => Maybe dData
        decData = J.decode $ BL.fromStrict fileContents
    case decData of
        Just d -> return d
        Nothing -> throwString ("Could not decode data file " <> P.toFilePath filePath)

-- Use ScopedTypeVariables
loadDomainData :: forall dData. J.FromJSON dData => FC.AbsFilePath -> IO dData
loadDomainData filePath = do
    fileContents <- readFileBinary $ P.toFilePath filePath
    let
        decData :: Maybe dData
        decData = J.decode $ BL.fromStrict fileContents
    case decData of
        Just d -> return d
        Nothing -> throwString ("Could not decode data file " <> P.toFilePath filePath)

-- No `decData` signature
loadDomainData :: J.FromJSON dData => FC.AbsFilePath -> IO dData
loadDomainData filePath = do
    fileContents <- readFileBinary $ P.toFilePath filePath
    let
        decData = J.decode $ BL.fromStrict fileContents
    case decData of
        Just d -> return d
        Nothing -> throwString ("Could not decode data file " <> P.toFilePath filePath)