Reputation: 11912
I want to case on a type variable that is restricted to finitely many possibilities due to a kind constraint. And I want to know statically that casing will always discover one of these finitely many possibilities. I can't figure out how to write this case without an unreachable catch-all.
As a concrete example, suppose I have a data kind
data{-kind-} Temp = Hot | Cold
Then my goal is write a function like caseTemp
below that determines the Temp
a given Temp
-kinded type. Something like
data CaseTemp (t :: Temp) where
IsHot :: CaseTemp 'Hot
IsCold :: CaseTemp 'Cold
caseTemp :: forall (t :: Temp). CaseTemp t
caseTemp = ???
I'm OK with having some extra constraints on caseTemp
, like the Typeable t
in my failed attempt below. Or even with an entirely different approach.
Here is my best attempt, but it includes a branch that I think should be unreachable, and that would allow caseTemp
to break silently if I added another constructor to Temp
(tested in GHC 8.0.2):
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StandaloneDeriving #-}
module SOQuestion where
import Data.Typeable ( (:~:)(..), Typeable, eqT )
data{-kind-} Temp = Hot | Cold
data CaseTemp (t :: Temp) where
IsHot :: CaseTemp 'Hot
IsCold :: CaseTemp 'Cold
deriving instance Show (CaseTemp t)
caseTemp :: forall (t :: Temp). Typeable t => CaseTemp t
caseTemp =
case eqT :: Maybe (t :~: 'Hot) of
Just Refl -> IsHot
Nothing -> case eqT :: Maybe (t :~: 'Cold) of
-- (GHC says this "pattern match is redundant" ???
-- Sounds like a bug!)
Just Refl -> IsCold
-- MY QUESTION: is there a way to eliminate the
-- unreachable branch here?
Nothing -> error "Unreachable!"
The problem with this attempt is that GHC believes the Nothing -> error "Unreachable!"
branch is reachable.
User @Alec mentions that Any :: Temp
is a fundamental reason that it's impossible to do what I want, since e.g.
import GHC.Prim ( Any )
[...]
badCase :: CaseTemp Any
badCase = undefined :: CaseTemp Any
is accepted by GHC. However, Any
is not Typeable
, so it's not clear to me that putting constraints on caseTemp
couldn't work around this.
Upvotes: 0
Views: 97
Reputation: 33389
There isn't a direct way, because when eqT
returns Nothing
it doesn't come with a disequality proof.
How about using a type class?
class IsTemp (b :: Temp) where
caseTemp :: CaseTemp b
Upvotes: 2