Programming Languages

Functional Programming in Haskell

Jim Posen - ECE/CS 2014

Functional Programming

  • We saw it with Racket
  • Recall that functional programs have limited side effects
  • A side effect is where a function application changes state or has an observable effect
    • Modifies a free variable, performs IO, etc

Side effects

def fib(n):
  x1 = 0
  x2 = 1
  for i in range(n):
    tmp = x2
    x2 = x1 + x2
    x1 = tmp
  return x1

No side effects

(define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1)) (fib (- n 2)))))

Functional programming benefits

  • Side effects made it difficult to analyze behavior
  • Functions with no side effects can be easily tested
  • Functions are referentially transparent
    • An expression can be replaced with its value

Haskell

  • Haskell is purely functional
  • High level constructs called monads are used to isolate side effects
  • This means values are immutable (with one exception)
  • Haskell uses lazy evaluation
  • Haskell is statically (and strongly) typed

Running Haskell

  • Haskell is compiled
  • GHC compiles to native machine code by default
  • GHCi is the interactive environment (REPL)

Haskell Syntax

Basic scalar types

  • Numbers
  • Chars
  • Bools

Numbers

Int, Integer, Rational, Float, etc

ghci> (50 * 100) - 4999
1
ghci> 50 * 100 - 4999
1
ghci> 50 * (100 - 4999)
-244950

Chars

Denoted with singe quotes

Bools

True or False

Function application

  • Generally uses prefix notation
  • Parenthesis are not necessary, but may be helpful
ghci> min 5 6 + max 10 20
25
ghci> min 5 max 3 4
-- Crash!
ghci> min 6 (max 3 4)
4

Function definitions

  • Functions are defined with =
  • Functions are defined over some parameters
  • 0-arity (no parameters) functions can be thought of as constants
  • Must use let in GHCi
ghci> let pi = 3.14
ghci> let area r = pi * r ^ 2
ghci> area 5
78.5
ghci> let hypot x y = sqrt (x ^ 2 + y ^ 2)
ghci> hypot 3 4
5.0

Let and where

  • Let and where create scoped variables
area r =
    let pi = 3.14
    in pi * r ^ 2
area r = pi * r ^ 2
    where pi = 3.14

Lists

  • Lists are linked lists, like in Lisp
  • Lists may only contain elements of one type
ghci> head [1, 2, 3, 4]
1
ghci> tail [1, 2, 3, 4]
[2, 3, 4]

List operations

  • head is like car
  • tail is like cdr
  • : is like cons
  • ++ concatenates lists
  • !! gets the nth element (zero-indexed)
ghci> 1:[2, 3, 4]
[1, 2, 3, 4]
ghci> [1, 2] ++ [3, 4]
[1, 2, 3, 4]
ghci> [1, 2] ++ [3, 4]
[1, 2, 3, 4]
ghci> [1, 2, 3, 4] !! 1
2

Strings

  • String literals denoted with double quotes
  • Strings are lists of chars
  • ghci> ['h', 'e', 'l', 'l', 'o'] ++ " world"
    "hello world"
    

Ranges

  • Simple syntax for generating ranges using ..
  • You can specify a step
ghci> [1..9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
ghci> [1, 3..9]
[1, 3, 5, 7, 9]
ghci> ['a'..'e']
"abcde"

Infinite ranges

  • Ranges can be infinite
  • Lazy evaluation makes this possible
ghci> let evens = [0, 2..]
ghci> evens !! 10
20

Tuples

  • Collections of elements
  • Fixed size
  • May contain different elements
ghci> (1, 'a', [3.0])
(1, 'a', [3.0])
ghci> zip [1, 2, 3] "abc"
[(1, 'a'), (2, 'b'), (3, 'c')]

Conditionals

Let’s define !! recursively

nth lst n =
    if n == 0
    then head lst
    else nth (tail lst) (n - 1)

Woohoo, it looks like Lisp

But we can do better…

Pattern matching

nth lst 0 = head lst
nth lst n = nth (tail lst) (n - 1)

Ooh, that’s pretty

But we can do better…

Even more pattern matching

Note: _ typically means ignored variable

nth (h:_) 0 = h
nth (_:t) n = nth t (n - 1)

We can even pattern match tuples

flipTuple (x, y) = (y, x)

Nifty syntax note

Backticks allow infix notation

ghci> nth [1, 2, 3] 1
ghci> [1, 2, 3] `nth` 1

Guards

  • Recall the Collatz sequence
    • n -> n/2 (n even)
    • n -> 3n + 1 (n odd)
    • 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
  • Can we write collatzNext with pattern matching?
(define (collatz-next n)
  (if (even? n)
      (/ n 2)
      (+ 1 (* 3 n))))

No, but we can use guards

collatzNext n =
    | even n = n / 2
    | otherwise = 3 * n + 1

List Comprehensions

  • Really nice syntax for transformations on lists

Let’s double all elements in a range

Lisp

(map (lambda (x) (* x 2)) (range 1 11))

Haskell

[x * 2 | x <- [1..10]]

Let’s double all multiples of 7 less than 100

Lisp

(map (lambda (x) (* x 2))
     (filter (lambda (x) (zero? (mod x 7)))
             (range 1 11)))

[x * 2 | x <- [1..10], x `mod` 7 == 0]

map, filter, and foldl still exist of course