编写形式`(forall a.Class a => Class(fa))=> Class(Type f)的实例

Mat*_*iak 9 haskell typeclass

我通过执行自然转换MyType Maybe来执行我的一个程序中的默认分辨率MyType Identity.我想ToJSON为这些类型派生一个实例.我知道Maybe并且Identity有实例ToJSON a => ToJSON (Maybe a)ToJSON a => ToJSON (Identity a).

我想声明以下形式的实例:

instance (forall a . ToJSON a => ToJSON (f a)) => ToJSON (MyType f)
Run Code Online (Sandbox Code Playgroud)

这似乎是提出类型系统的合理要求.我想表现出ToJSON例如MyType f,提供的,我总能得到ToJSON (f a)每一个ToJSON a.在逻辑表示法中,这就像说我可以证明某些属性P (P(a)⇒P(f(a)))⇒P(h(f)).这似乎很适合我.

不幸的是,我得到以下错误的语法:

• Illegal polymorphic type: forall a. ToJSON a => ToJSON (f a)
  A constraint must be a monotype
• In the context: (forall a. ToJSON a => ToJSON (f a))
  While checking an instance declaration
  In the instance declaration for ‘ToJSON (GpgParams f)’
Run Code Online (Sandbox Code Playgroud)

看起来QuantifiedConstraints提议会提供这种语法,但它尚未实现.

我可以尝试通过实现自己的类来解决这个约束JSONable.

class JSONable f where
  jsonize :: f a -> Value

  default jsonize :: (ToJSON a, ToJSON (f a)) => f a -> Value
  jsonize = toJSON
Run Code Online (Sandbox Code Playgroud)

不幸的是,这意味着放弃使用需要ToJSON约束的标准库中的所有函数.

据我所知,在这种情况下最好的权衡是简单地放弃并编写显式实例:

instance ToJSON (MyType Maybe)
instance ToJSON (MyType Identity)
Run Code Online (Sandbox Code Playgroud)

它是否真的像语言一样强大?所需的实例是否只是形成错误?或者实际上是否可以在Haskell中为现有的类型类声明这样的实例?

Li-*_*Xia 10

在QuantifiedConstraints到达之前,有一个标准的解决方案来编码约束forall a. ToJSON a => ToJSON (f a),就像你提到的那样,但我们不必放弃使用ToJSON约束的函数.

forall a. ToJSON a => ToJSON (f a)是一个约束f:我们可以将其定义为类型类.幸运的是,艾森已经拥有了ToJSON1.

class ToJSON1 f where  -- encoding of `forall a. ToJSON a => ToJSON (f a)`
  ...
Run Code Online (Sandbox Code Playgroud)

并且使用该类有一个功能

toJSON1 :: (ToJSON1 f, ToJSON a) => f a -> Value
Run Code Online (Sandbox Code Playgroud)

如果任何类型F具有实例ToJSON1 F,则预期其ToJSON实例等效于

instance ToJSON a => ToJSON (F a) where
  toJSON = toJSON1
Run Code Online (Sandbox Code Playgroud)

所以这ToJSON1 F确实编码forall a. ToJSON a => ToJSON1 (F a).

有一两件事,似乎遗漏对埃宋是解决办法ToJSON (f a)的约束给定ToJSON1 fToJSON a,但你也可以使用下面的NEWTYPE(中高kinded版本进行编码Identity):

newtype Id1 f a = Id1 { unId1 :: f a }

instance (ToJSON1 f, ToJSON a) => ToJSON (Id1 f a) where
  toJSON = toJSON1 . unId1
Run Code Online (Sandbox Code Playgroud)

然后定义ToJSON (MyType f),我们可以先申请coerce :: MyType f -> MyType (Id1 f).

import Data.Coerce

instance ToJSON1 f => ToJSON (MyType f) where
  toJSON = (...) . (coerce :: MyType f -> MyType (Id1 f))
    {- in "(...)" we can use functions that require "ToJSON (Id1 f a)", which is informally equivalent to "ToJSON (f a)" -}
Run Code Online (Sandbox Code Playgroud)