GeneralizedNewtypeDeriving should not require that constructor be in scope
As I learned perusing !1916 (closed) (but you don't need to read that MR), GeneralizedNewtypeDeriving requires that the constructor of the newtype in question be in scope. Yet this isn't really necessary:
A.hs:
module A where
newtype N1 = MkN1 N2
newtype N2 = MkN2 N1
instance Eq N2 where
(==) = const (const False)
B.hs:
{-# LANGUAGE DerivingStrategies, StandaloneDeriving, GeneralizedNewtypeDeriving,
DerivingVia #-}
module B where
import A ( N1, N2(..) )
import Data.Coerce
-- This fails:
-- deriving newtype instance Eq N1
-- This succeeds:
-- deriving via N2 instance Eq N1
-- This succeeds:
instance Eq N1 where
(==) = coerce ((==) :: N2 -> N2 -> Bool)
I would think that the three possible instance declarations in B.hs would all be completely equivalent. Yet the first is rejected while the last two are accepted.
This example is indeed silly, but it's possible to make mutually-recursive newtypes that are not silly (by using e.g. Maybe or Either to break the loop).
Conclusion: The newtype strategy should not require the newtype constructor to be in scope. Instead, it should rely on the Coercible machinery to trigger the usual case where we need the constructor in scope. Of course, we should make sure that the error message is sensible when this happens.