Aeson提供了FromJSON1ToJSON1类型类。这些类似于Eq1模块中定义的Show1Data.Functor.Classes类。

我对Eq1Show1类的理解是,它们需要能够在不使用FlexibleContextsUndecidableInstances扩展名的情况下表达对转换器参数的约束。

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


如果我们有Eq1f实例,Eqa实例,并且上面Eq1T 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




现在,Eq1Show1等类有意义。似乎可以更轻松地为转换器编写EqShow等的实例。

但是,我想知道Aeson中使用什么类型的FromJSON1ToJSON1?我很少有要使用JSON的转换器。

我最终更改为JSON的大多数数据类型是普通类型(不是类型构造函数)。也就是说,类型为*的类型。我还使用了Maybe之类的* -> *类型。

但是,我不认为我经常像上面的ToJSON那样为变压器创建FromJSONT实例。什么是经常用于往返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 adata Triple a = Triple a a adata 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抱怨它不知道如何比较aa'的相等性。因此,将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)), ...)都是可能的。

ToJSON1FromJSON1类起着类似的作用:它们给您一个约束,您可以赋予它一个无限的ToJSONFromJSON约束集合,以便您选择哪个ToJSONFromJSON约束,您需要以数据驱动的方式,并确保它可用。

10-02 02:21