我可以使用 DerivingVia 派生与元组同构的数据类型的实例吗

Can I use DerivingVia to derive instances for data types isomorphic to tuples

给定以下数据类型

data Both a b = Both { left :: a, right :: b }

我可以像这样为 Applicative 等编写实例(这里省略 Functor,因为我们可以使用 DeriveFunctor):

instance Monoid a => Applicative (Both a) where
    pure x = Both mempty x
    Both u f <*> Both v x = Both (u <> v) (f x)

由于Both同构于(a,b),我想知道是否可以使用DerivingVia来派生实例:

data Both a b = ... deriving Applicative via ((,) a)

这会导致错误消息,例如:

    • Couldn't match representation of type ‘(a, a1)’
                               with that of ‘Both a a1’
        arising from the coercion of the method ‘pure’
          from type ‘forall a1. a1 -> (a, a1)’
            to type ‘forall a1. a1 -> Both a a1’
    • When deriving the instance for (Applicative (Both a))

我将其解释为“编译器不知道如何将 Both 转换为 (,)”。我如何使用 显而易见的方式 告诉编译器做到这一点?

我已经看到 this question 和答案,但我希望有一个需要更少样板的解决方案。

受到this answer的启发,在generic-data包的帮助下,可以这样写:

{-# LANGUAGE DeriveGeneric, DerivingStrategies, DerivingVia #-}

import GHC.Generics
import Generic.Data

data Both a b = Both {left :: a, right :: b}
  deriving stock (Generic1)
  deriving (Functor, Applicative) via Generically1 (Both a)

DerivingVia paper 有一个部分是关于使用 Generic 为“通过 Generic 同构”的事物派生任意 类 的。参见 4.3.

感觉好像在hackage上看到过适配库的方法,但是现在好像找不到了。

但是,我不确定它是否适合您的情况,因为 (a, b) 和您的类型可能不具有相同的 Generic 表示(您的类型有记录字段)。 “数据类型通用手术”在这种情况下也可能有用 - https://github.com/Lysxia/generic-data-surgery#readme