Upgrade from GHC 8.10 to 9.2 - "Instance head cannot contain nested ‘forall’s or contexts"

Viewed 65

I have upgraded my codebase from GHC-8.10, to 9.2 this morning, and I am having trouble resolving an issue:

 Instance head cannot contain nested ‘forall’s or contexts
    In an instance declaration
    |
211 | instance (SingDSI padding =>  Bin.Binary (BinaryBlobPadded padding typ)) where

I have some code, that deals with binary-blobs, and can automatically pad them to either 32 or 64 bytes. I was trying to learn about TypeFamilies about 6 months ago, and thought this would be a good place to try them out, by encoding the padding. However, on returning to the code, its melting my brain a bit!

The original article I took insiration & code from was:

https://blog.jle.im/entry/introduction-to-singletons-1.html

which I adapted to my own case.

data BlockPadding = BlockPaddingNone
                    | BlockPadding32
                    | BlockPadding64
    deriving (Generic)
    deriving anyclass (NoThunks)
    deriving anyclass (NFData)


newtype BinaryBlobPadded (padding::BlockPadding) typ = BinaryBlobPadded W8.ByteString
    deriving (Show,Generic)
    deriving anyclass (NoThunks)
    deriving anyclass (NFData)

type BinaryBlobPad32 typ =  BinaryBlobPadded 'BlockPadding32 typ
type BinaryBlobPad64 typ =  BinaryBlobPadded 'BlockPadding64 typ


data SBlockPadding :: BlockPadding -> Type where
    SBlockPaddingNone :: SBlockPadding 'BlockPaddingNone
    SBlockPadding32   :: SBlockPadding 'BlockPadding32
    SBlockPadding64   :: SBlockPadding 'BlockPadding64

--Creates the following 3 values:
--SBlockPaddingNone :: SBlockPadding 'BlockPaddingNone
--SBlockPadding32   :: SBlockPadding 'BlockPadding32
--SBlockPadding64   :: SBlockPadding 'BlockPadding64

getBlockPadding :: SBlockPadding s -> (BinaryBlobPadded s typ) -> BlockPadding
getBlockPadding SBlockPaddingNone _ = BlockPaddingNone
getBlockPadding SBlockPadding32 _   = BlockPadding32
getBlockPadding SBlockPadding64 _   = BlockPadding64


class SingDSI s where
    singDS :: SBlockPadding s

instance SingDSI 'BlockPaddingNone where
    singDS = SBlockPaddingNone
instance SingDSI 'BlockPadding32 where
    singDS = SBlockPadding32
instance SingDSI 'BlockPadding64 where
    singDS = SBlockPadding64

getBlockPadding' :: SingDSI s => (BinaryBlobPadded s typ) -> BlockPadding
getBlockPadding' = getBlockPadding singDS


instance (SingDSI padding =>  Bin.Binary (BinaryBlobPadded padding typ)) where
     get = error "TODO"
     put block@(BinaryBlobPadded bytes) = do
         let n = W8.length bytes
         let p = calcNPadding (getBlockPadding' block) n
         Bin.Put.putByteString bytes
         Bin.Put.putByteString (W8.replicate p paddingValue)


paddingValue :: Word8
paddingValue = 0xEE

GHC is complaining about the instance, but I am not sure why. Is there a simple fix here, or am I going to need to do a major refactor here, to make the code compatible with GHC-9.2.

Many thanks for any insight, in advance.

1 Answers

You need to remove the outer set of parentheses from your instance declaration:

-- before
instance (SingDSI padding =>  Bin.Binary (BinaryBlobPadded padding typ)) where

-- after
instance SingDSI padding =>  Bin.Binary (BinaryBlobPadded padding typ) where

I don't remember seeing this change announced for GHC 9.0 or 9.2. Perhaps the parser is more strict now for some reason.

Here's a simpler example that shows the same problem:

newtype X a = X a
instance Eq a => Eq (X a) -- works
instance (Show a => Show (X a)) -- doesn't work
Related