Search code examples
haskellderivingderivingvia

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


Given the following data type

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

I can write instances for Applicative etc. like so (omitting Functor here as we can use DeriveFunctor):

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

Since Both is isomorphic to (a,b), I'm wondering whether I can use DerivingVia to derive the instance:

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

which results in error messages like:

    • 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))

which I interpret as "the compiler doesn't know how to turn Both into (,)". How do I tell the compiler to do that using the obvious way?

I've seen this question and the answers, but I'm hoping for a solution that requires less boilerplate.


Solution

  • Inspired by this answer, and with the help of the generic-data package one can write:

    {-# 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)