In-class-7 Test Review 1

This guide is here to help you review what we’ve covered so far in CSCE 314 before the midterm. Each section highlights key concepts, examples, and short review questions drawn from our weekly lectures. Use this as a way to refresh your understanding of Haskell’s functional programming foundations and how these ideas connect together.

Week 1 – History & Functional Programming Primer

Functional programming (FP) emerged as an alternative to imperative programming, emphasizing expressions and results rather than sequences of steps. FP languages like Haskell are based on mathematical functions and avoid mutable state.

total = sum arr -- Declarative: describe what, not how

Review Questions:

  1. What are the key differences between imperative and functional programming?
    • Imperative programming changes state step-by-step with commands; functional programming avoids state changes and builds results by applying pure functions.
  2. Why does Haskell use expressions rather than statements?
    • Because every construct in Haskell must produce a value — it emphasizes what to compute, not how to execute.

Week 3 – Algebraic Data Types (ADTs), Pattern Matching, Tuples, Guards

Algebraic Data Types (ADTs) allow us to define custom types that model real-world concepts directly. Pattern matching provides a clear, readable way to deconstruct these types.

data Shape = Circle Float | Rectangle Float Float
area (Circle r) = pi * r^2
area (Rectangle w h) = w * h

Review Questions:

  1. How do ADTs make programs more expressive?
    • ADTs (Algebraic Data Types) let you define custom types that model real-world concepts precisely, combining different shapes of data under one clear structure.
  2. What does the underscore (_) mean in a pattern match?
    • It’s a wildcard — it matches any value but ignores it (doesn’t bind it to a name).
  3. Tuples:
    • It is a group of items
    • A way of pairing different types of items together
    • Any amount of elements except for one, you cannot have a tuple of 1 item but you can have an empty tuple.
  4. Guards:
    • Alternative to if-statements
    • You can use these guards to protect the data from out of bounds values.

Week 4 – Recursion, Higher-Order Functions, Lambdas, Currying, Lists

Recursion follows the shape of data. Higher-order functions take or return other functions. Lambdas are anonymous functions, and currying lets you apply arguments one at a time.

sumList [] = 0
sumList (x:xs) = x + sumList xs
map (\x -> x * 2) [1..5] -- Example of lambda use

Review Questions:

  1. What are the base and recursive cases in a list recursion?
    • The base case handles the empty list ([]), and the recursive case processes the head (x) and recursively calls the function on the tail (xs).
  2. How does currying simplify function definitions?
    • Currying lets functions take arguments one at a time, enabling easy partial application and cleaner composition of smaller functions.

Week 5 – Polymorphism and Type Classes

Polymorphism allows functions to operate on values of many types. Type classes provide constraints, such as Eq for equality or Ord for ordering.

length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs

instance Eq Shape where
	(Circle r1) == (Circle r2) = r1 == r2
	_ == _ = False

Review Questions:

  1. What does it mean for a Haskell function to be polymorphic?
    • It means the function can operate on multiple types, using type variables (like a, b) instead of a fixed type.
  2. How are type classes similar to interfaces in OOP?
    • Both define a set of required behaviors — a type class specifies functions a type must implement, just like an interface defines methods a class must provide.

Week 6 – Laziness and Infinite Structures

Haskell uses lazy evaluation—values are computed only when needed. This enables infinite structures like streams and sequences.

naturals = [0..]
take 10 naturals -- [0,1,2,3,4,5,6,7,8,9]
drop 5 [0..] -- Skips the first 5 elements of an infinite list

Review Questions:

  1. How does lazy evaluation make infinite lists possible?
    • Lazy evaluation computes values only when needed, so Haskell can represent infinite lists without evaluating them fully.
  2. What does the drop function do conceptually?
    • It skips a given number of elements from the start of a list and returns the rest.

Week 7 – Trees in Functional Programming

Trees represent hierarchical data naturally in functional programming. Algebraic data types let us define recursive tree structures cleanly.

data Tree a = Leaf a | Node (Tree a) (Tree a)
height (Leaf _) = 1
height (Node l r) = 1 + max (height l) (height r)
inorder (Leaf x) = [x]
inorder (Node l r) = inorder l ++ inorder r

Review Questions:

  1. What is structural recursion, and how does it apply to trees?
    • It is just like a list, instead that it knows the tree has more structure to it, more things to evaluate.
    • Structural recursion breaks a structure into smaller parts of the same type; for trees, it processes a node and recursively calls itself on each subtree.
  2. How can the same tree structure represent different meanings (e.g., binary search vs. expression tree)?
    • You can have a tree of students, and you can one find out how many student we have by counting the nodes
      • What function would do this?
        • fold for trees.
    • What if I wanted to double the value of a tree?
      • fmap
      • Produces a new tree exact same shape, with all numbers doubled,
        • Not changing, but creating a second structure.
    • The data shape is the same, but the interpretation of nodes and values changes — one models data order, the other models computation.

Key Takeaways

Weeks 1–7 have built up a foundation in functional thinking, covering recursion, data abstraction, polymorphism, laziness, and structured data. These ideas combine to show how Haskell models computation through expressions and transformations rather than commands.