如何为类型 (* -> *) -> * 编写 aeson ToJSON 实例
How do I write aeson ToJSON instances for types with kind (* -> *) -> *
动机
我有一个类型 MyType
,它由仿函数 f
参数化。
我想用MyType Identity
表示数据的"my view",MyType Maybe
表示对数据的updates类型.
问题
是否可以为MyType
写一个aeson ToJSON
实例?我尝试使用 ToJSON
class,但出现错误(请参阅 post 的底部)。
{-# 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")
如何为任意 f
获取 MyType 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.
在利用 Lifting
class 的评论中使用我的想法,经过一些修补,我得出了这个
{-# 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
converts back and forth between constraints (such as ToJSON Int
) and values. Sub
只是约束蕴含的构造函数。
lifting @ToJSON @f @Int
是 type application syntax。
- 我通过查找
toJSON
的 default implementation 使用了 genericToJSON defaultOptions
。我们只需要先手动将一些实例纳入范围 lifting
。
希望对您有所帮助。
动机
我有一个类型 MyType
,它由仿函数 f
参数化。
我想用MyType Identity
表示数据的"my view",MyType Maybe
表示对数据的updates类型.
问题
是否可以为MyType
写一个aeson ToJSON
实例?我尝试使用 ToJSON
class,但出现错误(请参阅 post 的底部)。
{-# 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")
如何为任意 f
获取 MyType 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.
在利用 Lifting
class 的评论中使用我的想法,经过一些修补,我得出了这个
{-# 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
converts back and forth between constraints (such asToJSON Int
) and values.Sub
只是约束蕴含的构造函数。lifting @ToJSON @f @Int
是 type application syntax。- 我通过查找
toJSON
的 default implementation 使用了genericToJSON defaultOptions
。我们只需要先手动将一些实例纳入范围lifting
。
希望对您有所帮助。