Aeson提供了FromJSON1
和ToJSON1
类型类。这些类似于Eq1
模块中定义的Show1
和Data.Functor.Classes
类。
我对Eq1
和Show1
类的理解是,它们需要能够在不使用FlexibleContexts
和UndecidableInstances
扩展名的情况下表达对转换器参数的约束。Data.Functor.Classes
模块中文档中的示例如下:
假设我们有一个充当转换器的数据类型:T
。例如,让它与IdentityT
同构:
data T f a = T (f a)
T
的种类如下:T :: (* -> *) -> * -> *
如果
Eq1
有一个f
实例,则在为Eq1
编写T f
实例时可以使用它:instance Eq1 f => Eq1 (T f) where
liftEq :: (a -> b -> Bool) -> T f a -> T f b -> Bool
liftEq eq (T fa1) (T fa2) = liftEq eq fa1 fa2
如果我们有
Eq1
的f
实例,Eq
的a
实例,并且上面Eq1
的T f
实例在范围内,我们可以轻松地为Eq
编写T f a
实例>:instance (Eq1 f, Eq a) => Eq (T f a) where
(==) :: T f a -> T f a -> Bool
(==) = eq1
eq1
的类型定义如下:eq1 :: (Eq1 h, Eq a) => h a -> h a -> Bool
在上面的例子中,
h
变为T f
,因此eq1
的类型可以认为如下:eq1 :: Eq a => T f a -> T f a -> Bool
现在,
Eq1
,Show1
等类有意义。似乎可以更轻松地为转换器编写Eq
,Show
等的实例。但是,我想知道Aeson中使用什么类型的
FromJSON1
和ToJSON1
?我很少有要使用JSON的转换器。我最终更改为JSON的大多数数据类型是普通类型(不是类型构造函数)。也就是说,类型为
*
的类型。我还使用了Maybe
之类的* -> *
类型。但是,我不认为我经常像上面的
ToJSON
那样为变压器创建FromJSON
或T
实例。什么是经常用于往返JSON的转换器?我会错过一些有用的变压器吗? 最佳答案
Eq1
提供了另一个您在博览会中没有讨论的功能:它使您可以编写一个以许多不同类型调用(==)
的函数,而不必事先知道要使用哪种类型的函数。
我举一个玩具的例子。希望您可以通过本示例的明显无用来理解Eq1
赋予您一些有趣功能的原因。
想象一下,您想制作一棵在分支因子上参数化的树,因此您可以通过子容器对其进行参数化。因此值可能如下所示:
{-# LANGUAGE GADTs #-}
data Tree m a where
Branch :: Tree m (m a) -> Tree m a
Leaf :: a -> Tree m a
例如,我可以得到带有
Tree Pair
的二叉树,带有Tree Triple
的三叉树,带有Tree TwoThree
的指形树和带有Tree []
的玫瑰树,其中data Pair a = Pair a a
,data Triple a = Triple a a a
和data TwoThree a = Two a a | Three a a a
。现在,我想为此编写一个Eq
实例。如果仅依靠Eq
约束,那么我们将无法到达目的地。我们试试吧:instance Eq (Tree m a) where
Leaf a == Leaf a' = a == a'
Branch t == Branch t' = t == t'
_ == _ = False
自然,GHC抱怨它不知道如何比较
a
和a'
的相等性。因此,将Eq a
添加到上下文中:instance Eq a => Eq (Tree m a) where ...
现在,GHC抱怨说,它不知道在
m a
情况下如何比较Branch
的相等性。说得通。instance (Eq a, Eq (m a)) => Eq (Tree m a) where ...
还是不走!现在,
(==) :: Tree m a -> Tree m a -> Bool
的实现在其(==) :: Tree m (m a) -> Tree m (m a) -> Bool
情况下具有对Branch
的递归调用,因此必须提供上下文(Eq (m a), Eq (m (m a)))
来进行该递归调用。好的,让我们将其添加到实例上下文中...instance (Eq a, Eq (m a), Eq (m (m a))) => Eq (Tree m a) where ...
还是不好。现在,递归调用必须证明更多的东西!我们真正想说的是,如果我们有
Eq b
,那么对于所有Eq (m b)
而言,我们都有b
,而不仅仅是用作a
第二参数的特定Tree
。instance (Eq a, (forall b. Eq b => Eq (m b))) => Eq (Tree m a) where ...
当然,在Haskell中这完全不是问题。但是
Eq1
给我们的是:instance Eq1 m => Eq1 (Tree m) where
liftEq (==) (Leaf a) (Leaf a') = a == a'
liftEq (==) (Branch t) (Branch t') = liftEq (liftEq (==)) t t'
liftEq (==) _ _ = False
instance (Eq1 m, Eq a) => Eq (Tree m a) where
(==) = eq1
在这里,
Eq1 m
约束起到了我们之前要求的作用,即所有(Eq a, Eq (m a), Eq (m (m a)), ...)
都是可能的。ToJSON1
和FromJSON1
类起着类似的作用:它们给您一个约束,您可以赋予它一个无限的ToJSON
和FromJSON
约束集合,以便您选择哪个ToJSON
或FromJSON
约束,您需要以数据驱动的方式,并确保它可用。