Reputation: 2089
For the types Door
and Hallway
:
data DoorState :: Type where
Opened :: DoorState
Closed :: DoorState
Locked :: DoorState
deriving (Bounded, Enum, Eq, Ord, Show)
data Door :: DoorState -> Type where
Door :: {material :: String} -> Door s
deriving (Show)
data Hallway :: [DoorState] -> Type where
Origin :: Hallway '[]
Section :: Door ds -> Hallway dsl -> Hallway (ds : dsl)
This definition of appendHallway
works:
appendHallway :: forall (ds :: DoorState) (dsl :: [DoorState]). Door ds -> Hallway dsl -> Hallway (ds : dsl)
appendHallway d rest = Section d rest
However this definition for appendHallway
where the relationship between ds
and dsl
is being explicitly indicated in the forall
section does not work:
appendHallway :: forall t (ds :: t) (dsl :: [t]). (t ~ DoorState) => Door ds -> Hallway dsl -> Hallway (ds : dsl)
appendHallway d rest = Section d rest
The error that is returned is as follows:
error:
• Expected kind ‘DoorState’, but ‘ds’ has kind ‘t’
• In the first argument of ‘Door’, namely ‘ds’
In the type signature:
appendHallway :: forall t (ds :: t) (dsl :: [t]).
(t ~ DoorState) => Door ds -> Hallway dsl -> Hallway (ds : dsl)
|
351 | appendHallway :: forall t (ds :: t) (dsl :: [t]). (t ~ DoorState) => Door ds -> Hallway dsl -> Hallway (ds : dsl)
| ^^
The example above may be a little contrived, but there might be situations where indicating relationships between higher kinded type variables would be helpful or even necessary. Is this error a limitation of the current version of GHC or is the above nonsensical even in a future version of GHC? Is there another way to express the relationship between ds
and dsl
that would be accepted by GHC?
Upvotes: 1
Views: 105
Reputation: 29193
The thing you have written is indeed nonsense. The constraints on the LHS of a =>
exist only on the value-level, in the same way things on the LHS of a ->
exist only on the value-level. More specifically (though this is a faint memory), an instance of a ~ b
"contains" within it a primitive type of kind a ~# b
(in the same way a data Showable = forall s. Show s => Showable s
holds a type of kind Type
). You need the a ~# b
to actually get anything done, but you'd need to unpack the a ~ b
to get it. You cannot talk about the a ~ b
, a value-level argument, in a type, though, in the same way you cannot talk about the DoorState
in DoorState -> Door _can'tTalkAboutTheDoorState
.
What you can do is this. Define
type family Cast (x :: (a :: Type)) :: (b :: Type) where
Cast x = x
-- this seems strange, but realize the equation is actually
-- Cast @a @a (x :: a) = (x :: a)
-- Cast-ing some type from one kind to another only goes through
-- when they're actually the same kind
-- can make a, b, or both explicit instead of implicit
Then
appendHallway :: forall t (ds :: t) (dsl :: [t]).
(t ~ DoorState) =>
Door (Cast ds) ->
Hallway (Cast dsl) ->
Hallway (Cast ds : Cast dsl)
appendHallway d rest = Section d rest
Whenever t ~ DoorState
is known, the type family applications Cast @t @DoorState ds
and Cast @[t] @[DoorState] dsl
reduce to ds
and dsl
respectively.
Upvotes: 2
Reputation: 152997
Haskell has separate namespaces for computations, types, and kinds. When you write
forall (ds :: t). ...
the variable t
is the kind-level variable, but when you write
t ~ DoorState => ...
the variable t
is the type-level variable t
, a completely unrelated variable. Indeed, all type equalities are at the type-level only; as far as I know there is no way to express kind equalities as constraints at all in current GHC Haskell.
Upvotes: 2