haskelltype-familiestype-kindsconstraint-kindspolykinds

Unhelpful Kind equality error at the start of file


I get an error

app\Main.hs:1:1: error:
    Couldn't match kind `*' with `Constraint'
    When matching types
      b :: *
      (Set b, Set s) :: Constraint
  |
1 | {-# LANGUAGE TypeFamilies #-}
  | ^

I don't know why b and the constraint (Set b, Set s) are being matched? I would expect the constraint to existentially quantify the type b but why would it be matching them?

I believe the last thing I changed before getting the error was adding OpOutcome to the class.

here is the code

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RankNTypes #-}


module Main where

import GHC.TypeLits (Symbol)
import GHC.Exts (Constraint)
import Data.Reflection (Reifies, reflect)
import Data.Proxy (Proxy(..))

main :: IO ()
main = print 5


class ((a,b)::Constraint) => HasCtxt a b
instance (a,b) => HasCtxt a b
class Determines a b | a -> b
instance Determines a b => Determines a b
type Set b = (b ~ b)

type OpLayout a = (forall s ctxt b. (OpCtxt a s b ~ ctxt, Determines a b, Determines a ctxt,Reifies s b) => ( HasCtxt ctxt (Reifies s b))) :: Constraint


data Stack a where
  Cons :: OpConstructor a -> Stack b -> Stack a
  Nil  :: Stack "NIL"


class  OpLayout a => OpCode (a::Symbol) where
  type OpCtxt a s b = (ctxt :: Constraint) |  ctxt -> s b
  type OpOutcome a :: *
  data OpConstructor a
  opValue :: Determines a s => Proxy s
  opValue = Proxy
  popOP :: OpCtxt a s b => Stack a -> (b :: *)
  evalOP :: OpConstructor a -> Stack x -> OpOutcome a

instance OpCode "load_val" where
  type OpCtxt "load_val" s b  = (Set s, Set b)
  type OpOutcome "load_val" = Stack "load_val"
  data OpConstructor "load_val" = forall b. LOAD_VAL b
  popOP stack = reflect opValue
  evalOP op stack = op `Cons` stack

edit: smaller version, thanks to Krzysztof Gogolewski

{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE PolyKinds #-}

module Err where

import GHC.Exts (Constraint)

class Determines b | -> b
class (forall (b :: *) (c :: Constraint). (Determines b, Determines c) => c) => OpCode
instance OpCode

Solution

  • Here's a much smaller file that has essentially the same error:

    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE QuantifiedConstraints #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE FunctionalDependencies #-}
    {-# LANGUAGE UndecidableInstances #-}
    {-# LANGUAGE PolyKinds #-}
    {-# LANGUAGE FlexibleContexts #-}
    
    module Test where
    
    import GHC.Exts (Constraint)
    
    class Determines a b | a -> b
    
    class  (forall ctxt. (Determines a a, Determines a (OpCtxt a)) => ctxt) => OpCode a where
        type OpCtxt a :: Constraint
    
    instance OpCode ()
    

    This gives:

    test.hs:1:1: error:
        Couldn't match kind ‘Constraint’ with ‘*’
        When matching types
          OpCtxt () :: Constraint
          () :: *
      |
    1 | {-# LANGUAGE TypeFamilies #-}
      | ^
    

    (By the way, in the future, you should probably try to do some similar minization yourself before asking on here.)

    It's certainly a bug that GHC reports the error with such unhelpful location information. However, there's now so few places for a problem to lurk that we can have a pretty good idea what's happening. The situation here is that we are asking for

    Determines a a
    Determines a (OpCtxt a)
    

    with a functional dependency in scope that says a should be enough to work out the other argument to Determines. Well, we know a has *, because we're writing instance OpCode () and () :: *; and we know OpCtxt a has kind Constraint, because we said so in the class declaration. So GHC gives up on trying to unify a and OpCtxt a before it even gets started -- they don't have the same kinds, so can't be equal!

    The only trick you need left to see why you get the exact error message you do is to stick () for a everywhere (because that's the instance we're trying to write).

    Translating back to your setting: you ask for

    Determines a b
    Determines a ctxt
    

    where we know b :: * because it appears as the second argument to Reifies :: k -> * -> Constraint, and we know ctxt :: Constraint because we said so in the OpCode class declaration. So the functional dependency can't possibly work out right. Then you get the error you saw by putting OpCtxt "load_val" s b for ctxt, and then reducing to (Set s, Set b).