Programming Languages

Typing Systems with Haskell

Jim Posen - ECE/CS 2014

Type Systems

  • A type system is the model a programming language uses to assign types to values or variables
  • Static vs dynamic typing
  • Strong vs weak typing
  • Type safety properties

Static vs dynamic type checking

  • Are the types known without running the program?
    • Type checkers perform static analysis
    • Are the types known during compilation?
  • Do variables have types?
  • Languages may have optional type declarations

Staticly typed languages

  • Java
  • C/C++
  • SML (functional language)

Dynamicly typed languages

  • Python
  • Racket
  • JavaScript

Hybrids

  • Common Lisp
    • Dynamic by default with optional type declarations

Strong vs weak typing

  • Do values/objects have types?
  • Higher standard of type safety
  • Weakly typed languages have less type safety
  • Not as clear cut as static/dynamic type checking
  • I know it when I see it

Strongly typed languages

  • Java
    • ints can be added to Strings
    • Is this an exception?
  • Python
    • TypeError often thrown
  • Racket
    • Rationals vs fixnums vs booleans vs symbols

Weakly typed languages

  • C
    • int a = *((int*) b);
  • JavaScript
  • PHP

Wat

  • Weak languages often have more unexpected behavior

Typing Chart

Strong Weak
Static Java C
Dynamic Python JavaScript

What is Haskell?

Back to Haskell

Prelude> "hello " ++ "world"
"hello world"
Prelude> "hello " ++ True

:9:13:
    Couldn't match expected type `[Char]' with actual type `Bool'
    In the second argument of `(++)', namely `True'
    In the expression: "hello " ++ True
    In an equation for `it': it = "hello " ++ True

OK, there is type safety, so Haskell must be strongly typed

  • The above code won’t even compile
  • Haskell is staticly typed

Type inference

  • Haskell infers the types of variables
  • If variable types can’t be inferred, you will get a compilation error
  • Type declarations can be used to remove ambiguity
  • By convention, functions types are declared

Haskell types

  • Use :t to determine type
  • Bool, Char, Int, Float, Integer, Double are some basic types
  • [] denotes list
    • [Int] is a list of Ints
    • [Char] is a String
    • This is why you can’t mix types in lists
  • (Int, Bool) is a tuple with and Int and a Bool

Type declarations

  • Use :: to declare type
  • Type declarations can be used to remove ambiguity
Prelude> 5 :: Float
5.0
Prelude> True :: Float
:17:1:
    Couldn't match expected type `Float' with actual type `Bool'
    In the expression: True :: Float
    In an equation for `it': it = True :: Float

Also declare the types of variables

pi :: Double
pi = 3.1415

Function types

  • Function types are determined by parameter types and return type
  • Parameter and return types separated by -> arrows
sumThree :: Int -> Int -> Int -> Int
sumThree x y z = x + y + z

Let’s annotate some functions

factorial n = product [1..n]
factorial :: Int -> Integer
firstLetter (x:xs) = x
firstLetter :: [Char] -> Char
fibHelper (a, b) 0 = a
fibHelper (a, b) 1 = b
fibHelper (a, b) n = fibHelper (b, a + b) (n - 1)
fibHelper :: (Integer, Integer) -> Int -> Integer

Type variables

  • What is the type of head ?
    • head :: [Char] -> Char
    • head :: [Int] -> Int
    • head :: [Bool] -> Bool
  • We use a type variable for the type
  • head :: [a] -> a
  • a represents any type
  • Type variables are like generics in Java

What is the type of zip ?

zip :: [a] -> [b] -> [(a, b)]

Type classes

How about this function that tests equality and returns 1 or 0

foo :: a -> a -> Int
foo x y = if x == y then 1 else 0

Why won’t this compile?

  • What if we can’t check the equality of values of type a?
  • We need to impose a constraint on the type variable a
  • We use type classes for this

Type classes

  • Types belong to type classes
  • Similar to interfaces in Java
  • Below we enforce type variable a is of typeclass Eq
foo :: (Eq a) => a -> a -> Int
foo x y = if x == y then 1 else 0
  • Types of class Ord have comparison operators defined
  • show :: (Show a) => a -> String is defined on types with class Show
  • Int, Integer, Float, Double are Nums
  • Int, Integer are Integrals
  • Float, Double are Fractionals

Partial Function Application

Lambdas

  • Haskell has nice syntax for anonymous functions
addThree x = x + 3
addThree = (\x -> x + 3)

Currying

  • In lambda calculus, functions take one parameter
  • How do you create a multivariable function?
  • Currying!

Currying

  • Currying is a way to convert a function with n parameters into a function returning a function with n - 1 parameters

Here is an example without currying

sumThree x y z = x + y + z

Here is the curried form

sumThree = (\x -> (\y -> (\z -> x + y + z))

Currying meets typing

What is the type of the uncurried form?

sumThree :: (Num a) => a -> a -> a -> a
sumThree x y z = x + y + z

What is the type of the curried form?

sumThree :: (Num a) => a -> (a -> (a -> a))
sumThree = (\x -> (\y -> (\z -> x + y + z))

What if I told you -> was right associative?

That would mean

sumThree :: (Num a) => a -> (a -> (a -> a))

is equivalent to

sumThree :: (Num a) => a -> a -> a -> a

Mind Blowing Slide No. 1

  • All functions in Haskell take one parameter and are curried
  • Haskell syntax makes it easy to forget this sometimes
  • What does this mean?
  • ghci> let sumThree x y z = x + y + z
    ghci> ((sumThree 1) 2) 3
    6
    

Partial function application

  • If a function is called with fewer parameters than it takes, it is partially applied
  • You get back a new function that takes the remaining parameters
  • This is super useful

Partial application example

Let’s write a function that returns a number if it is positive or 0 if it’s negative

makeNatural = (Num a) => a -> a
makeNatural x = max 0 x

Partial application example

How about?

makeNatural = (Num a) => a -> a
makeNatural = max 0

You can even partially apply infix functions

addThree = (Num a) => a -> a
addThree = (+3)

Function composition

  • . operator composes functions
  • (f . g) x = f (g x)
ghci> map (negate . abs) [-1, 2, -4, 5]
[-1, -2, -4, -5]

Function application operator

  • Usually function application is performed using spaces
  • $ is a lower precedence operator
  • ghci> map (2*) (filter (< 5) [1..])
    [2, 4, 6, 8]
    ghci> map (2*) filter (< 5) [1..]
    -- Crash!
    ghci> map (2*) $ filter (< 5) [1..]
    -- Crash!
    

One more example

Let’s tighten up our qsort example

qsort [] = []
qsort (p:xs) = (qsort $ filter (<= p) xs) ++ [p] ++ (qsort $ filter (> p) xs)