{-# LANGUAGE GADTs, TypeFamilies #-} module Types where data Zero data Suc n data Vec a n where Nil :: Vec a Zero Cons :: a -> Vec a n -> Vec a (Suc n) type family Add m n :: * type instance Add Zero n = n type instance Add (Suc m) n = Suc (Add m n) {- Task (a): Give the signature and implementation of |(++)| for vector concatenation and explain why it type checks. -} (+++) :: Vec a m -> Vec a n -> Vec a (Add m n) Nil +++ ys = ys Cons x xs +++ ys = Cons x (xs +++ ys) -- Nil :: Vec a Zero -- return type = Vec a (Add Zero n) = Vec a n = the type of ys -- Cons x xs :: Vec a (Suc m) -- return type = Vec a (Add (Suc m) n) = Vec a (Suc (Add m n)) = type of the RHS {- Would it still type check with the alternative definition of type-level addition below? Why/why not? -} type family Add' m n :: * type instance Add' m Zero = m type instance Add' m (Suc n) = Suc (Add' m n) {- No, it would not type check. The case split in the function definition must match the case split in the type family, otherwise the type unification will fail. It is possible to work around it using a family for type equality and a coerce function. -} -- Task (b): Implement a GADT |Fin n| for unary numbers below |n| and -- a lookup function |(!) :: Vec a n -> Fin n -> a|. data Fin n where Fzero :: Fin (Suc n) Fsuc :: Fin n -> Fin (Suc n) (!) :: Vec a n -> (Fin n -> a) Cons x xs ! Fzero = x Cons x xs ! (Fsuc i) = xs ! i {- Task (c): Briefly explain the Curry-Howard correspondence for ``false'', ``true'', ``implies'', ``and'', ``or''. ---- The Curry-Howard correspondence provides a "Logic" reading of types: Types Logic p :: P p is a proof of P empty type False non-empty type True P -> Q P implies Q (P, Q) P and Q Either P Q P or Q -- Not asked for in the exam: (a :: A) -> P ∀a:A. P -- dependent function type (a :: A, P) ∃a:A. P -- dependent pair type -} -- ................ -- Test code - not part of the exam v1 = Cons 1 (Cons 7 Nil) v2 = Cons 3 (Cons 8 Nil) long = v1 +++ v2 two = Fsuc (Fsuc Fzero) main = print (long ! two == 3)