ill*_*out 4 haskell typeclass higher-kinded-types aeson
Aeson提供FromJSON1并ToJSON1键入类。这些类似于模块中定义的Eq1和Show1类Data.Functor.Classes。
我对Eq1and Show1类的理解是,它们需要能够表达对转换器参数的约束,而无需使用诸如FlexibleContextsand的扩展名UndecidableInstances。
该Data.Functor.Classes模块文档中的示例如下:
假设我们有一个充当转换器的数据类型:T。举个例子,让我们与IdentityT:同构:
data T f a = T (f a)
Run Code Online (Sandbox Code Playgroud)
种类T如下:
T :: (* -> *) -> * -> *
Run Code Online (Sandbox Code Playgroud)
如果存在的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
Run Code Online (Sandbox Code Playgroud)
如果我们有的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
Run Code Online (Sandbox Code Playgroud)
的类型eq1定义如下:
eq1 :: (Eq1 h, Eq a) => h a -> h a -> Bool
Run Code Online (Sandbox Code Playgroud)
在上述示例中,h变为T f,因此eq1可以将类型视为以下类型:
eq1 :: Eq a => T f a -> T f a -> Bool
Run Code Online (Sandbox Code Playgroud)
现在,Eq1,Show1,等类的意义。看起来这使得它更容易编写的情况下Eq,Show等变压器。
不过,我想知道什么类型的FromJSON1,并ToJSON1在埃宋是干什么用的?我很少有要使用JSON的转换器。
我最终更改为JSON的大多数数据类型是普通类型(不是类型构造函数)。也就是说,类型与实物有关*。我也使用类似Maybe的类型* -> *。
不过,我不认为我经常创建ToJSON或FromJSON实例的变压器,像T上面。什么是通常用于往返JSON的转换器?我会错过一些有用的变压器吗?
Eq1 offers another feature that you haven't discussed in your exposition: it lets you write a function that calls (==) at many different types, without necessarily knowing ahead of time which types you will use it on.
I'll give a toy example; hopefully you can see through the apparent uselessness of this example to the reason Eq1 gives you some interesting powers.
Imagine you want to make a tree that is parameterized on the branching factor, so you parameterize it by the child container. So values might look like this:
{-# LANGUAGE GADTs #-}
data Tree m a where
Branch :: Tree m (m a) -> Tree m a
Leaf :: a -> Tree m a
Run Code Online (Sandbox Code Playgroud)
For example, I can get binary trees with Tree Pair, trinary trees with Tree Triple, finger trees with Tree TwoThree, and rose trees with Tree [], where data Pair a = Pair a a, data Triple a = Triple a a a, and data TwoThree a = Two a a | Three a a a. Now I would like to write an Eq instance for this. If we only rely on Eq constraints, we can't get where we want to go. Let's try:
instance Eq (Tree m a) where
Leaf a == Leaf a' = a == a'
Branch t == Branch t' = t == t'
_ == _ = False
Run Code Online (Sandbox Code Playgroud)
Naturally, GHC complains that it doesn't know how to compare a and a' for equality. So add Eq a to the context:
instance Eq a => Eq (Tree m a) where ...
Run Code Online (Sandbox Code Playgroud)
Now GHC complains that it doesn't know how to compare m as for equality in the Branch case. Makes sense.
instance (Eq a, Eq (m a)) => Eq (Tree m a) where ...
Run Code Online (Sandbox Code Playgroud)
Still no go! Now the implementation of (==) :: Tree m a -> Tree m a -> Bool has a recursive call to (==) :: Tree m (m a) -> Tree m (m a) -> Bool in its Branch case, hence must provide the context (Eq (m a), Eq (m (m a))) to make that recursive call. Okay, let's add that to the instance context...
instance (Eq a, Eq (m a), Eq (m (m a))) => Eq (Tree m a) where ...
Run Code Online (Sandbox Code Playgroud)
Still no good. Now the recursive call has to prove even more stuff! What we'd really like to say is that if we have Eq b, then we have Eq (m b), for all bs and not just for the specific a being used as Tree's second parameter.
instance (Eq a, (forall b. Eq b => Eq (m b))) => Eq (Tree m a) where ...
Run Code Online (Sandbox Code Playgroud)
Of course that's totally not a thing in Haskell. But Eq1 gives us that:
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
Run Code Online (Sandbox Code Playgroud)
Here the Eq1 m constraint is serving the role we asked for before, namely, that all of (Eq a, Eq (m a), Eq (m (m a)), ...) are possible.
The ToJSON1 and FromJSON1 classes serve a similar role: they give you a single constraint that you can give that amounts to a potentially infinite collection of ToJSON and FromJSON constraints, so that you can choose which ToJSON or FromJSON constraint you need in a data-driven way and be guaranteed that it's available.