{-# LANGUAGE
TypeFamilies,
MultiParamTypeClasses,
FlexibleInstances #-}
data True = T
data False = F
class LogicOr a b where
type Or a b
typeOr :: a -> b -> Or a b
instance LogicOr True b where
type Or True b = True
typeOr T b = T
instance LogicOr a True where
type Or a True = True
typeOr b T = T
instance LogicOr False False where
type Or False False = False
typeOr F F = F
class Decide tf a b where
type If tf a b
nonFunctionalIf :: tf -> a -> b -> If tf a b
instance Decide True a b where
type If True a b = a
nonFunctionalIf T a b = a
instance Decide False a b where
type If False a b = b
nonFunctionalIf F a b = b
whatIsMyType tf1 tf2 a b = nonFunctionalIf (typeOr tf1 tf2) a b
If we load this into ghci, we have the following example:
*Main> :t whatIsMyType F F 2 "foo"
whatIsMyType F F 2 "foo" :: (Num t) => If (Or False False) t [Char]
*Main> :t whatIsMyType F F 2 "foo" :: String
whatIsMyType F F 2 "foo" :: String :: String
In the first :t, ghci lazily (though correctly) gives us the type of whatIsMyType. In the second example, where we provide an explicit type annotation, ghci is able to (correctly) conclude that our annotation was correct by evaluating the various type functions it had given us before.