Deriving via: When do Representations Match?

I have a datatype used to aggregate some statistics, it looks like this:

data DataType = DataType
  { a :: Int
  , b :: Int
  }

Furthermore, I would like to use it for folds, so a semigrop/monoid instance would be useful, which I imagined I might get like this:

  deriving (Semigroup, Monoid) via (Sum Int, Sum Int)

or this

  deriving (Semigroup, Monoid) via Generically (Sum Int, Sum Int)

But it doesn’t work, the error message says they are not equivalent:

1. • Couldn't match representation of type ‘(Sum Int, Sum Int)’
                              with that of ‘DataType’

However, using a newtype does work:

newtype NewType = NewType (Int, Int)
  deriving stock (Show, Generic)
  deriving (Semigroup, Monoid) via (Sum Int, Sum Int)

Just to be sure I wasn’t having some obvious mistake lingering in my code, I pasted the Rep of the datatypes into a text-differ, which looked like this.


I’m very sorry if the quality is very poor on your display, I didn’t know how to properly format the diff using this markdown.
Nevertheless, at least concerning the generics, they seem to representationally equivalent! (All the differences are metadata or newtype wrappers, which are equivalent when the Constructor is in Scope)

Whilst searching around for solutions I discovered this discussion which recommended Generic.Data.Surgery

Unaware of this the Surgery module, I had hacked together my own Solution using Coercible constraints on Generic Representations.

Leading up to the question in the title, when are two datatype representations considered equivalent by GHC, allowing for DerivingVia?

3 Likes

The ghc users guide provided me with an answer.

To use two types in a DerivingVia clause, they have to be Coercible in terms of coerce.

1 Like

Note that

 deriving (Semigroup, Monoid) via Generically DataType

Should also work.

Edit: No, I was wrong! I forgot that for that to work automatically we would need Semigroup and Monoid intances for Int.

3 Likes