{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Bug where
import Data.Bifunctor
import Data.Bifunctor.TH
import Data.Kind
type family F :: Type -> Type -> Type
type instance F = Either
newtype T a b = MkT (F a b)
One can derive a Bifunctor instance for T using GeneralizedNewtypeDeriving:
deriving newtype instance Bifunctor T
Trying to use Template Haskell to accomplish the same thing, however, fails:
$(deriveBifunctor ''T)
Bug.hs:1:1: error:
Exception when trying to run compile-time code:
Constructor ‘MkT‘ must only use its last two type variable(s) within the last two argument(s) of a data type
CallStack (from HasCallStack):
error, called at src/Data/Bifunctor/TH.hs:903:32 in bifunctors-5.5.7-382d87a82a5fe85a191a6a3943a4d9c453af2598fed3436d948a78b02f584473:Data.Bifunctor.TH
Code: deriveBifunctor ''T
|
1 | {-# LANGUAGE DerivingStrategies #-}
| ^
The issue is that deriveBifunctor is too conservative: it bails out if the last type variables (a/b) occur as an argument to any type family whatsoever. Although F is a type family, its use in MkT is benign, since the type F a b oversaturates F (it has zero type variable binders). deriveBifunctor should take the number of type variable binders a type family has into account so as to permit the program above.
Consider the following setup:
One can derive a
Bifunctor
instance forT
usingGeneralizedNewtypeDeriving
:Trying to use Template Haskell to accomplish the same thing, however, fails:
The issue is that
deriveBifunctor
is too conservative: it bails out if the last type variables (a
/b
) occur as an argument to any type family whatsoever. AlthoughF
is a type family, its use inMkT
is benign, since the typeF a b
oversaturatesF
(it has zero type variable binders).deriveBifunctor
should take the number of type variable binders a type family has into account so as to permit the program above.