ekmett / bifunctors

Haskell 98 bifunctors, bifoldables and bitraversables
Other
57 stars 42 forks source link

Deriving Bifunctor et al. chokes on type families with an appropriate return kind #82

Closed RyanGlScott closed 4 years ago

RyanGlScott commented 4 years ago

Consider the following setup:

{-# 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.