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.
CodePudding user response:
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)
CodePudding user response:
The DerivingVia paper has a section on using Generic
to derive arbitrary classes for things that are "isomorphic via Generic
". See 4.3.
I feel like I've seen the approach adapted to a library on hackage, but I can't seem to find it now.
However, I'm not sure it would work for your case, as (a, b)
and your type might not have the same Generic
representation (your type has record fields).
"data type generic surgery" could also be useful in that case - https://github.com/Lysxia/generic-data-surgery#readme