Skip to content

DerivingVia fails to roundtrip through TH

The following programs works on GHC HEAD, as expected:

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
module Bug where

newtype List a = MkList [a]
deriving via forall a. [a] instance Eq a => Eq (List a)

However, if you declare the standalone-derived instance using Template Haskell quotes:

$([d| deriving via forall a. [a] instance Eq a => Eq (List a) |])

Then it fails to typecheck:

[1 of 1] Compiling Bug              ( Bug.hs, interpreted )

Bug.hs:8:2: error:
    • Couldn't match representation of type ‘a’ with that of ‘a1’
        arising from a use of ‘GHC.Prim.coerce’
      ‘a’ is a rigid type variable bound by
        the instance declaration
        at Bug.hs:8:2-65
      ‘a1’ is a rigid type variable bound by
        the instance declaration
        at Bug.hs:8:2-65
    • In the expression:
        GHC.Prim.coerce
          @([] a_a3ek -> [] a_a3ek -> Bool) @(List a -> List a -> Bool)
          ((==) @([] a_a3ek))
      In an equation for ‘==’:
          (==)
            = GHC.Prim.coerce
                @([] a_a3ek -> [] a_a3ek -> Bool) @(List a -> List a -> Bool)
                ((==) @([] a_a3ek))
      When typechecking the code for ‘==’
        in a derived instance for ‘Eq (List a)’:
        To see the code I am typechecking, use -ddump-deriv
      In the instance declaration for ‘Eq (List a)’
    • Relevant bindings include
        (==) :: List a -> List a -> Bool (bound at Bug.hs:8:2)
  |
8 | $([d| deriving via forall a. [a] instance Eq a => Eq (List a) |])
  |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Bug.hs:8:2: error:
    • Couldn't match representation of type ‘a’ with that of ‘a1’
        arising from a use of ‘GHC.Prim.coerce’
      ‘a’ is a rigid type variable bound by
        the instance declaration
        at Bug.hs:8:2-65
      ‘a1’ is a rigid type variable bound by
        the instance declaration
        at Bug.hs:8:2-65
    • In the expression:
        GHC.Prim.coerce
          @([] a_a3ek -> [] a_a3ek -> Bool) @(List a -> List a -> Bool)
          ((/=) @([] a_a3ek))
      In an equation for ‘/=’:
          (/=)
            = GHC.Prim.coerce
                @([] a_a3ek -> [] a_a3ek -> Bool) @(List a -> List a -> Bool)
                ((/=) @([] a_a3ek))
      When typechecking the code for ‘/=’
        in a derived instance for ‘Eq (List a)’:
        To see the code I am typechecking, use -ddump-deriv
      In the instance declaration for ‘Eq (List a)’
    • Relevant bindings include
        (/=) :: List a -> List a -> Bool (bound at Bug.hs:8:2)
  |
8 | $([d| deriving via forall a. [a] instance Eq a => Eq (List a) |])
  |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Eek. What went wrong? -ddump-splices reveals the answer:

[1 of 1] Compiling Bug              ( Bug.hs, interpreted )
Bug.hs:8:2-65: Splicing declarations
    [d| deriving via forall a_a3g8. [a_a3g8] instance Eq a_a3g8 =>
                                                      Eq (List a_a3g8) |]
  ======>
    deriving via forall a_a3gW. [a_a3gW] instance Eq a_a3g8 =>
                                                  Eq (List a_a3g8)

By the time the declaration has been spliced in, the a in the [a] part is no longer the same a as in the Eq a => Eq (List a) part. Crucially, these must be the same in order for this instance to work.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information