如何为类型为(*-> *)-> *的类型编写aeson ToJSON实例

时间:2018-06-23 21:20:14

标签: haskell aeson

动机

我有一个类型MyType,该类型由函子f参数化。

我想用MyType Identity代表数据的“我的观点”,用MyType Maybe代表数据的更新的类型。

问题

是否可以为ToJSON编写aeson MyType实例?我尝试使用ToJSON类,但出现错误(请参阅文章底部)。

{-# LANGUAGE DeriveGeneric #-}
module Main where

import GHC.Generics
import Data.Aeson

data MyType f = MyType
  { age  :: f Int
  , name :: f String
  } deriving(Generic)

instance ToJSON1 f => ToJSON (MyType f)

main :: IO ()
main = print . encode $ MyType (Just 1) (Just "hi")

如何为任意MyType f获取f的ToJSON实例?

编译错误

Main.hs:12:10: error:
    • Could not deduce (ToJSON (f String))
        arising from a use of ‘aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$dmtoJSON’
      from the context: ToJSON1 f
        bound by the instance declaration
        at Main.hs:12:10-39
    • In the expression:
        aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$dmtoJSON @MyType f
      In an equation for ‘toJSON’:
          toJSON = aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$dmtoJSON @MyType f
      In the instance declaration for ‘ToJSON (MyType f)’
   |
12 | instance ToJSON1 f => ToJSON (MyType f)
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.

1 个答案:

答案 0 :(得分:2)

在利用Lifting类的评论中使用我的想法,经过一番修补后,我得出了这个结论

{-# LANGUAGE DeriveGeneric
           , FlexibleContexts   
           , MultiParamTypeClasses
           , ScopedTypeVariables
           , TypeApplications
           , UndecidableInstances 
           #-}
module Main where

import GHC.Generics
import Data.Aeson
import Data.Constraint
import Data.Constraint.Lifting

data MyType f = MyType
  { age  :: f Int
  , name :: f String
  } deriving(Generic)

instance (Lifting ToJSON f) => ToJSON (MyType f) where
    toJSON mt
        | Sub Dict <- lifting @ToJSON @f @Int
        , Sub Dict <- lifting @ToJSON @f @String
            = genericToJSON defaultOptions mt

instance Lifting ToJSON Maybe where
    lifting = Sub Dict

main :: IO ()
main = print . encode $ MyType (Just 1) (Just "hi")

注意:

  • Dict在约束(例如ToJSON Int)和值之间来回转换。 Sub只是约束约束的构造器。
  • lifting @ToJSON @f @Inttype application syntax
  • 我通过查找genericToJSON defaultOptions的{​​{3}}来使用toJSON。我们只需要首先使用lifting手动将一些实例纳入范围。

我希望这会有所帮助。