Playing with Type Level Literals as a way to discriminate non empty container values (as with phantom types) using a Maybe like type.
This works well. (required GHC >= 7.6.1)
But trying to define a binary function (eq)
eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym :: Symbol) a -> Bool
that admits different groups of values, signals a compile error when it is used:
Couldn't match type "Just"' with"Nothing"'
{-# LANGUAGE DataKinds, KindSignatures, GADTs, FlexibleInstances #-} 
import GHC.TypeLits
data TMaybe :: Symbol -> * -> * where
  TNothing  :: TMaybe "Nothing" a
  TJust :: a -> TMaybe "Just" a
nonEmpty :: Maybe a -> TMaybe "Just" a
nonEmpty (Just x) = TJust x
nonEmpty Nothing = error "invalid nonEmpty data"
-- this fromJust rejects TNothing at compile time  
fromJust :: (sym ~ "Just") => TMaybe (sym :: Symbol) a -> a
fromJust (TJust x) = x
tmbToMaybe :: TMaybe (sym :: Symbol) a -> Maybe a
tmbToMaybe TNothing = Nothing
tmbToMaybe (TJust x) = Just x
mbToTNothing Nothing = TNothing
mbToTJust (Just x) = TJust x
instance Eq a => Eq (TMaybe (sym :: Symbol) a) where
     TNothing == TNothing = True
     TJust x == TJust y = x == y
     _ == _ = False    -- useless, equal types required
instance Ord a => Ord (TMaybe (sym :: Symbol) a) where
     compare TNothing TNothing = EQ
     compare (TJust x) (TJust y) = Prelude.compare x y
     compare TNothing _ = LT   -- useless, equal types required
     compare _ TNothing = GT   -- useless, equal types required
instance  Functor (TMaybe (sym :: Symbol))  where
    fmap _ TNothing       = TNothing
    fmap f (TJust a)      = TJust (f a)
instance  Monad (TMaybe "Just") where
    (TJust x) >>= k      = k x
    (TJust _) >>  k      = k
    return            = TJust
    fail _              = error "can't fail to TNothing"
--------------------------
-- defining eq to admit parameter types with different symbol
eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym :: Symbol) a -> Bool
eq TNothing TNothing = True
eq (TJust x) (TJust y) = x == y
eq _ _ = False
---------------------------
-- Test
main = do
        print $ fromJust $ TJust (5::Int)   -- type-checks
        -- print $ fromJust TNothing   -- as expected, does not type-check
        -- print $ TNothing == TJust (5::Int)  -- as expected, does not type-check, types required equal at function def.
        print $ TNothing `eq` TJust (5::Int)   -- does not type-check either
                Well, your type
eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym :: Symbol) a -> Bool
demands that both arguments have the same type, so of course the compiler will reject an attempt to compare a TMaybe "Nothing" a and a TMaybe "Just" a.
If you change the type to
eq :: (Eq a) => TMaybe (sym :: Symbol) a -> TMaybe (sym1 :: Symbol) a -> Bool
it compiles and
TNothing `eq` TJust (5::Int)
evaluates to False. (You then need to explicitly determine the type of TNothings in many places, though.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With