Com S 541 Lecture -*- Outline -*- * types in Haskell (Ch 9 in Thompson, Ch 4 in Davie) ad: more systematic treatment of Haskell types we've been using. ** type operators (Davie 4.1) ------------------------------------------ TYPE NOTATION Type declarations x :: Integer f :: Integer -> Integer Type operators operator meaning ============================= _ -> _ function type (_ , _) product type [ _ ] list type Associativity b f g x means (((b f) g) x) a -> b' -> c means a -> (b' -> c) ------------------------------------------ Q: Why do you think the associtivity is different for applications and for function types? Might mention connection to logic... ** polymorphic types (Thompson 9.2, Davie 4.2) ------------------------------------------ POLYMORPHIC TYPES Monomorphic examples: Integer [Bool] -> Bool [(Integer, Integer) -> Bool] Polymorphic examples: [a] [b] -> b [(c,c) -> Bool] ------------------------------------------ Q: What are some expressions that have these types? Explain in terms of universal quantifiers. Q: What are some other instances of these types? ** type synonyms (Davie 4.3.1) ------------------------------------------ TYPE SYNONYMS Examples > type Nat = Int > type TextString = [(Nat, String)] > type Stack a = [a] > type Queue a = [a] > type MyQueue a = Queue a > type Predicate a = (a -> Bool) ------------------------------------------ The semantics is that these are just abbreviations. Q: Does this allow us to pass a (Stack Int) to a function of type [Int] -> Int? ** algebraic types (Thompson 10, Davie 4.4) (just mention) ------------------------------------------ ALGEBRAIC TYPES Can simulate enumerations > data Font = Roman | Italic | Bold data Color = data Boolean = Can also be used to define recursive types, including data HaskellType = ------------------------------------------ ... Red | Blue | Yellow ... True | False Note that the constructor names must start with upper case letter! ... including grammars ... Unit | BuiltIn String | Synonym String | List HaskellType | HaskellType `Arrow` HaskellType | Tuple [HaskellType] | Data String ** abstract data types (Thompson 16, Davie 4.5, 4.9) ------------------------------------------ ABSTRACT DATA TYPES -- file Fraction.hs module Fraction (Fraction, mkFraction, num, denom, add, sub) where data Fraction = Integer :/ Integer mkFraction _ 0 = error "undefined" mkFraction n d = n :/ d num (n :/ d) = n denom (n :/ d) = d add (n1 :/ d1) (n2 :/ d2) = mkFraction (n1 * d2 + n2 * d1) (d1 * d2) sub (n1 :/ d1) (n2 :/ d2) = mkFraction (n1 * d2 - n2 * d1) (d1 * d2) ------------------------------------------ If we had written module Fraction (Fraction(:/), ...) then the constructor :/ would have been exported. ------------------------------------------ FREE TYPES VS. TYPES MODULO LAWS def: a data type is *free* (*algebraic) if Examples: def: a data type is *not free* (abstract) if Examples: ------------------------------------------ ... any value manufactured using the constructor functions is legal (according to your idea of what the type should be) e.g., Stack, [a], ... ... some values manufactured using the constructor functions are not legal (according to your idea of what the type should be) e.g. Fraction, Set, PrimeNumber Usually, you have to define your own notion of equality if the type is not free. sometimes you have a choice of how to define the type e.g., NonEmptyList Q: Is it ever worthwhile to hide the representation of a free type? ** overview of type inference (Thompson 13, Davie 4.7) ------------------------------------------ OVERVIEW OF TYPE INFERENCE Type checking: you declare type, compiler infers type, compiler compares Type inference: compiler infers type In Haskell don't need to declare types (usually) Example > mymap f [] = [] > mymap f (x:xs) = (f x):(mymap f xs) ------------------------------------------ do the inference (quickly, we'll see more later) mymap :: a = (b -> c) f :: b etc. since the variables are unconstrained, this is polymorphic ** ad hoc polymorphism and type classes (Thompson 12, Davie 4.8) See paper by Wadler and Blott (POPL 89) ------------------------------------------ AD HOC POLYMORPHISM parametric polymorphism: map :: (a -> b) -> [a] -> [b] ad hoc polymorphism > square :: Num a => a -> a > square x = x * x ------------------------------------------ the point is that square x only works on those x for which * is defined it's illuminating to make a parametric version of this: squareP :: (a -> a -> a) -> a -> a squareP mult x = x `mult` x So what square needs to be polymorphic, is an additional "capability", the appropriate multiplication routine. Q: Why not require that you actually pass the multiplication yourself? Q: What's done in OO programming? lookup the method, based on the run-time type this isn't the run-time type, but the static type (note that there's no subtyping in Haskell, we know exact type of everything) But the idea is similar, we group types with related sets of operations into type classes, the type context (Num a =>) gives the name of the type class required *** type classes (Thompson 12.4) ------------------------------------------ TYPE CLASSES IN HASKELL -- abbreviated Eq type class class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x==y) -- abbreviated Ord type class class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a ------------------------------------------ there is a "default method" provided for /=, and the ordering stuff This is just like an "abstract class" in OOP, but remember it's *static* overloading data Ordering = LT | EQ | GT deriving (Eq, Ord, Ix, Enum, Read, Show, Bounded) The Ord type class is a *subclass* of Eq, it inherits defs from Eq, really if you think of these as requirements, it's a refinement (stronger requirement) on clients. There are also various ways to declare that a type you make up is an instance of a type class. *** type class instances (Thompson 12.3) ------------------------------------------ DECLARING TYPE CLASS INSTANCES > data Prod a b = a :* b > instance (Eq a, Eq b) > => Eq (Prod a b) where > (x :* y) == (x' :* y') = > (x == x' && y == y') or you can write: > data Cartesian a b = a :** b deriving Eq ------------------------------------------ pitfall: apparently the "where" can't be at the same indentation level as "instance", although I don't see why... *** higher-order type classes ------------------------------------------ HIGHER-ORDER TYPE CLASSES -- from the Prelude class Functor f where fmap :: (a -> b) -> (f a -> f b) instance Functor [] where fmap g [] = [] fmap g (x:xs) = g x : fmap g xs data Maybe a = Nothing | Just a deriving (Eq, Ord, Read, Show) instance Functor Maybe where fmap g Nothing = Nothing fmap g (Just x) = Just (g x) ------------------------------------------ Thus fmap has the type (a -> b) -> ([a] -> [b]) The type Maybe a is often used with functions that would otherwise have errors Other instances of Functor from the Prelude instance Functor ((->) a) where fmap f g x = f (g x) (in the Prelude this is instance Functor ((->) a) where fmap = (.) these are equivalent due to the definition of (.)) How this works in practice: Prelude> fmap not not not . not :: Bool -> Bool Prelude> fmap not not True Bool_True :: Bool Prelude> fmap (3+) ((-)4) 10 -3 :: Integer Note that we have Prelude> 3 + (4 - 10) -3 :: Integer How does it type check? We must have fmap :: (c -> d) -> (((->) a) c) -> (((->) a) d) or rather (changing to infix) fmap :: (c -> d) -> (a -> c) -> (a -> d) But this is just the type of the composition operator (.); so it type checks immediately. Another example: > inc :: (Functor f, Num a) => f a -> f a > inc xs = fmap (+1) xs With this, try the following: inc (Just 3) inc [3, 4]