首页 > 解决方案 > 将 Aeson 解码为泛型类型时遇到问题

问题描述

这是我第一次尝试使用 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
[..]

我错过了什么?感谢四位的任何见解!

标签: jsonhaskelltypeclassaeson

解决方案


您根本不需要类型注释。没有它就不能编译吗?

您缺少的是letorwhere子句中类型签名中的变量不在包含函数的类型签名范围内。因此, for 签名中的类型变量dData与 for 签名中的loadDomainData完全无关。GHC 抱怨输入的类型没有实例,因为类型签名说它没有实例。您可以添加它:dDatadecDatadecDataJ.FromJSON

decData :: J.FromJSON dData => Maybe dData

或者您可以打开ScopedTypeVariables扩展并修改包含函数的类型签名以将dData变量标记为作用域:

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

同时保持与decData以前相同的声明( noforall和 no 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)

推荐阅读