{-
Make several parallels between the type and level value in Haskell.
First , we have unlimited functions both in type and in value. At the type level, you can express almost everything using type families. You cannot match correspondence to arbitrary functions either by type or by value. For example. you cannot say
type family F (a :: *) :: * type family IsF (f :: * -> *) :: Bool where IsF F = True IsF notF = False -- Illegal type synonym family application in instance: F -- In the equations for closed type family 'IsF' -- In the type family declaration for 'IsF'
Second , we have fully implemented data and type constructors, such as Just 5 :: Maybe Integer at the value level or Just 5 :: Maybe Nat at the level level.
On them, you can match the correspondence:
isJust5 :: Maybe Integer -> Bool isJust5 (Just 5) = True isJust5 _ = False type family IsJust5 (x :: Maybe Nat) :: Bool where IsJust5 (Just 5) = True IsJust5 x = False
Note the difference between arbitrary functions and type / data constructors. The property of constructors is sometimes called generation. For two different functions f and g it is quite possible that fx = gx for some x . On the other hand, for the constructors fx = gx it follows f = g . This difference makes the first case (pattern matching on arbitrary functions) unsolvable, and the second case (pattern matching on fully applicable constructors) is decidable and accessible.
So far, we have seen consistency in type and value.
Finally , we partially applied (including not applicable) constructors. At the type level, they include Maybe , IO and [] (unapplied), as well as Either String and (,) Int (partially applied). At the value level, we do not use Just and Left and partially apply (,) 5 and (:) True .
The generativity condition does not care if the application is full or not; therefore, there is nothing to exclude matching patterns for partially used constructors. This is what you see at the type level; and we could have it at the level of values.
type family IsLeft (x :: k -> k1) :: Bool where IsLeft Left = True IsLeft x = False isLeft :: (a -> Either ab) -> Bool isLeft Left = True isLeft _ = False -- Constructor 'Left' should have 1 argument, but has been given none -- In the pattern: Left -- In an equation for 'isLeft': isLeft Left = True
The reason this is not supported is efficiency. Type level calculations are performed at compile time in interpreted mode; therefore, we can allow ourselves to transfer a lot of metadata about the types and types of functions to match the template according to them.
Value-level calculations are compiled and should be as fast as possible. Saving enough metadata to support pattern matching application designers will complicate the compiler and slow down the program at run time; it's too much to pay for such an exotic feature.