【问题标题】:Trouble type-checking Aeson decoding into generic types将 Aeson 解码为泛型类型时遇到问题
【发布时间】:2020-05-19 12:24:12
【问题描述】:

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

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

【问题讨论】:

    标签: json haskell typeclass aeson


    【解决方案1】:

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

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

    decData :: J.FromJSON dData => Maybe dData
    

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

    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)
    

    【讨论】:

    • 非常感谢!我确实不知道函数类型的签名不会延续到实际的函数定义中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多