aeson中的FromJSON1和ToJSON1是干什么用的?
What are FromJSON1 and ToJSON1 used for in aeson?
Aeson 提供 FromJSON1
and ToJSON1
type classes. These are similar to the Eq1
and Show1
classes defined in the Data.Functor.Classes
模块。
我对 Eq1
和 Show1
类 的理解是,它们需要能够在不使用 FlexibleContexts
和 FlexibleContexts
和UndecidableInstances
.
Data.Functor.Classes
模块文档中的示例如下:
假设我们有一个充当转换器的数据类型:T
。例如,让它同构于 IdentityT
:
data T f a = T (f a)
种类T
如下:
T :: (* -> *) -> * -> *
如果 f
有一个 Eq1
实例,可以在为 T f
编写 Eq1
实例时使用它:
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
如果我们有 f
的 Eq1
实例,a
的 Eq
实例,以及 T f
的 Eq1
实例以上在范围内,我们可以轻松地为 T f a
:
编写 Eq
实例
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 抱怨它不知道如何在 Branch
情况下比较 m a
s 是否相等。有道理。
instance (Eq a, Eq (m a)) => Eq (Tree m a) where ...
还是不行!现在 (==) :: Tree m a -> Tree m a -> Bool
的实现在其 Branch
情况下对 (==) :: Tree m (m a) -> Tree m (m a) -> Bool
进行了递归调用,因此必须提供上下文 (Eq (m a), Eq (m (m a)))
以进行该递归调用。好的,让我们将其添加到实例上下文中...
instance (Eq a, Eq (m a), Eq (m (m a))) => Eq (Tree m a) where ...
还是不行。现在递归调用必须证明更多的东西!我们真正想说的是,如果我们有 Eq b
,那么对于所有 b
,我们就有 Eq (m 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
约束,这样您就可以以 data-driven 的方式选择您需要的 ToJSON
或 FromJSON
约束,并确保它可用。
Aeson 提供 FromJSON1
and ToJSON1
type classes. These are similar to the Eq1
and Show1
classes defined in the Data.Functor.Classes
模块。
我对 Eq1
和 Show1
类 的理解是,它们需要能够在不使用 FlexibleContexts
和 FlexibleContexts
和UndecidableInstances
.
Data.Functor.Classes
模块文档中的示例如下:
假设我们有一个充当转换器的数据类型:T
。例如,让它同构于 IdentityT
:
data T f a = T (f a)
种类T
如下:
T :: (* -> *) -> * -> *
如果 f
有一个 Eq1
实例,可以在为 T f
编写 Eq1
实例时使用它:
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
如果我们有 f
的 Eq1
实例,a
的 Eq
实例,以及 T f
的 Eq1
实例以上在范围内,我们可以轻松地为 T f a
:
Eq
实例
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 抱怨它不知道如何在 Branch
情况下比较 m a
s 是否相等。有道理。
instance (Eq a, Eq (m a)) => Eq (Tree m a) where ...
还是不行!现在 (==) :: Tree m a -> Tree m a -> Bool
的实现在其 Branch
情况下对 (==) :: Tree m (m a) -> Tree m (m a) -> Bool
进行了递归调用,因此必须提供上下文 (Eq (m a), Eq (m (m a)))
以进行该递归调用。好的,让我们将其添加到实例上下文中...
instance (Eq a, Eq (m a), Eq (m (m a))) => Eq (Tree m a) where ...
还是不行。现在递归调用必须证明更多的东西!我们真正想说的是,如果我们有 Eq b
,那么对于所有 b
,我们就有 Eq (m 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
约束,这样您就可以以 data-driven 的方式选择您需要的 ToJSON
或 FromJSON
约束,并确保它可用。