json - 将 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
[..]
我错过了什么?感谢四位的任何见解!
解决方案
您根本不需要类型注释。没有它就不能编译吗?
您缺少的是let
orwhere
子句中类型签名中的变量不在包含函数的类型签名范围内。因此, for 签名中的类型变量dData
与 for 签名中的loadDomainData
完全无关。GHC 抱怨输入的类型没有实例,因为类型签名说它没有实例。您可以添加它:dData
decData
decData
J.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)
推荐阅读
- solidity - debot fetch 和 debot start 有什么区别?
- python - 查找重复元素的第一个索引
- isabelle - 案例 vs case_tac/induct vs induct_tac
- python - 在 python 中,仅提取文件的一部分时,如何确保单词保持在一行并且没有重复?
- python - Python交叉乘法与任意数量的列表
- python - Kivy Python:与类和实例方法混淆
- c++ - 为什么不使用 std::move 一切?
- python - 如何在 UWSGI 进程之间共享持久对象?
- swift - 如何在swift中使用来自异步函数的数据退出for循环?
- jupyter-notebook - 为什么我在本地运行 jupyter notebook 绘制 qiskit 量子电路看起来不一样