-- in GHCi, we can use :t to get the type of something -- :t "Hello!" "Hello!" :: [Char] -- It's good practice to give functions type declarations removeNonUppercase :: String -> String removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']] addThree :: Int -> Int -> Int -> Int addThree x y z = x + y + z -- Common types: -- Int - normal integer -- Integer - big integer, less efficient -- Float - real floating point, single precision -- Double - real floating point, double precision -- Bool - True or False -- Char - single character -- Type variables - write polymorphic functions that don't use the type-specific -- features of the values passed in: -- :t fst fst :: (a,b) -> a -- :t head head :: [a] -> a -- `a` is the type-variable here -- => denotes a class constraint. e.g. :t (==) (==) :: (Eq a) => a -> a -> Bool -- This says (==) takes a and a to Bool, given that the a's are members of the Eq class -- Another example: -- :t elem elem :: (Eq a) => a -> [a] -> Bool -- Eq - classes support equality testing -- Ord - types have an ordering -- e.g. :t (>) (>) :: (Ord a) => a -> a -> Bool -- To be a member of Ord, you must be a member of Eq and have defined behavior for the `compare` function. -- Show - members can be presented as strings -- Read - opposite of show - takes a string and returns a type that is a member of Read -- e.g. restTest1 = read "[1,2,3,4]" ++ [3] -- == [1,2,3,4,3] -- The way the result of `read` is used determines what typeclass the value is instanced in -- :t read read :: (Read a) => String -> a -- the `a` variable is determined by the usage. We can be explicit: readTest2 = read "5" :: Int -- a is Int -- Enum - sequential types that can be enumerated - Enum types can be used in list ranges -- Bounded - have upper and lower bounds -- e.g. minBoundTest1 = minBound :: Int maxBoundTest1 = maxBound :: Char -- these have type (Bounded a) => a - essentially polymorphic constants -- the value changes depending on the typecast -- Num - members can act like numbers, e.g. -- :t 20 20 :: (Num t) => t -- :t (*) (*) :: (Num a) => a -> a -> a -- Integral - only whole numbers - superset including Int and Integer -- Floating - only floating point numbers - Float and Double -- Useful note: fromIntegral takes an Integral to a generic Num type -- :t fromIntegral fromIntegral :: (Num b, Integral a) => a -> b