【问题标题】:FromJSON instance with DataKinds带有 DataKinds 的 FromJSON 实例
【发布时间】:2016-12-17 18:37:06
【问题描述】:

尝试使用 TypeLits 对数据类型进行 JSON 反序列化,但遇到以下问题:

无法将类型“n”与“2”匹配 ‘n’ 是一个刚性类型变量,由 test.hs:14:10 处的实例声明 预期类型:aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser (X n) 实际类型:aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser (×2)

在以下示例中,在 FromJSON 实例中一般允许 Nat 的正确语法如何:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

import GHC.TypeLits
import Data.Aeson
import Control.Monad (mzero)

data X (n :: Nat) where
  A :: Integer -> X 1
  B :: Integer -> X 2

instance FromJSON (X n) where
  parseJSON (Object o) = do
      v <- o .: "val"
      t <- o .: "type"
      case t of
        "a" -> return $ A v
        "b" -> return $ B v
  parseJSON _       = mzero

【问题讨论】:

  • 您可能需要FromJSON (X 1)FromJSON (X 2) 的单独实例,或者对n 进行类型化(使用单例)。您编写实例的方式表明,您可以为 调用者 选择的任何n 读取X n

标签: haskell aeson data-kinds


【解决方案1】:

由于您显然无法在编译时知道要反序列化的类型,因此需要将确切的类型隐藏在存在型中,然后通过模式匹配来恢复。我通常使用通用的Some 类型来隐藏幻像类型。

{-# LANGUAGE PolyKinds #-}

data Some (t :: k -> *) where
    Some :: t x -> Some t

现在你可以把实例写成

instance FromJSON (Some X) where
    parseJSON (Object o) = do
        v <- o .: "val"
        t <- o .: "type"
        case (t :: String) of
          "a" -> return $ Some $ A v
          "b" -> return $ Some $ B v
    parseJSON _       = mzero

但是,您还需要启用FlexibleInstances 扩展。

【讨论】:

  • 如果您愿意,可以使用专门的data SomeX = forall n . SomeX (X n) 来避免FlexibleInstances。通用版本让你赢得了什么?
  • 当然,在这个简单的例子中什么都没有。但是,我想包含更通用的版本,因为您通常在应用程序中有几种不同的类型,您希望从中暂时隐藏类型参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-06
  • 1970-01-01
  • 2016-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多