[Haskell-cafe] Is UndecidableInstances unavoidable in heterogenous Show derivation?

Viktor Dukhovni ietf-dane at dukhovni.org
Sat Nov 30 19:22:19 UTC 2019

I was reading:

    Summer School on Generic and Effectful Programming
    Applying Type-Level and Generic Programming in Haskell
    Andres Löh, Well-Typed LLP


and trying out the various code fragments, and was mildly surprised
by the need for -XUndecidableInstances when deriving the 'Show'
instance for the heterogenous N-ary product 'NP':

    deriving instance All (Compose Show f) xs => Show (NP f xs)

Without "UndecidableInstances" I get:

    • Variable ‘k’ occurs more often
        in the constraint ‘All (Compose Show f) xs’
        than in the instance head ‘Show (NP f xs)’
      (Use UndecidableInstances to permit this)
    • In the stand-alone deriving instance for
        ‘All (Compose Show f) xs => Show (NP f xs)’

This is not related to automatic deriving, as a hand-crafted instance triggers
the same error:

    instance All (Compose Show f) xs => Show (NP f xs) where
      show Nil = "Nil"
      show (x :* xs) = show x ++ " :* " ++ show xs

Is there some other way of putting together the building blocks that avoids the
need for 'UndecidableInstances', or is that somehow intrinsic to the type of NP

I guess I should also ask whether there's a way to define something equivalent
to 'Compose' without 'UndecidableSuperClasses', and perhaps the two are not

[ Perhaps I should be more blasé about enabling these extensions, but I prefer
  to leave sanity checks enabled when possible. ]


{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}

import GHC.Exts (Constraint)

-- | Map a constraint over a list of types.
type family All (c :: k -> Constraint) (xs :: [k]) :: Constraint where
    All _ '[] = ()
    All c (x ': xs) = (c x, All c xs)

-- | With @g@ a type constructor and @f@ a constraint, define @Compose f g@ as
-- a contraint on @x@ to mean that @g x@ satisfies @f at .
-- Requires:
--   'ConstraintKinds'
--   'FlexibleInstances'
--   'MultiParamTypeClasses'
--   'TypeOperators'
--   'UndecidableSuperClasses'
class (f (g x)) => (Compose f g) x
instance (f (g x)) => (Compose f g) x

-- | Type-level 'const'.
newtype K a b = K { unK :: a }
deriving instance Show a => Show (K a b)

-- | N-ary product over an index list of types @xs@ via an interpretation
-- function @f@, constructed as a list of elements @f x at .
data NP (f :: k -> *) (xs :: [k]) where
    Nil  :: NP f '[]
    (:*) :: f x -> NP f xs -> NP f (x ': xs)
infixr 5 :*

-- | If we can 'show' each @f x@, then we can 'show' @NP f xs at .
-- Requires: 'UndecidableInstances'
deriving instance All (Compose Show f) xs => Show (NP f xs)

More information about the Haskell-Cafe mailing list