Scala for the Impatient

Case Classes and Pattern Matching A2

Copyright © Cay S. Horstmann 2015

Understand the match statement

A Better Switch

Variables in Patterns

Type Patterns

Appreciate the similarity between the match statement and exception catching

Catching Exceptions

Work with extractors

Extractors

Extractors in Variable Declarations

Design and use case classes

Case Classes

Case Classes

Work with the Option type

Example: Option

Know how to choose between case classes and polymorphism

Recursive Data Structures

Case Classes or Polymorphism?

What Didn't We Cover?

Practice pattern matching, case classes, and using the Option type

Lab

Scary looking lab

Part 1: Pattern Matching

  1. Using pattern matching, write a function swap that receives a pair of integers and returns the pair with the components swapped.
  2. Using pattern matching, write a function swap that receives an Array[Int] and returns the array with the first two elements swapped, provided the length is at least two.

Part 2: Articles and Bundles

  1. A store sells items. Some are articles. Others are bundles of articles—stuff that you buy together for a discount.
    abstract class Item
    case class Article(description: String, price: Double) extends Item
    case class Bundle(description: String, discount: Double, items: Item*) extends Item
    
    Declare an Article: the book “Scala for the Impatient” for $39.95:
    val book = ...
  2. Declare a bundle, sold at a $10 discount: the book from before, and a bottle of Old Potrero Straight Rye Whiskey for $79.95:
    val gift = ...
  3. Write a function price(it: Item): Double that computes the price of an item. Use pattern matching. Start with filling in the case for Article. What is your code?
  4. What happens when you call price(book)? price(gift)?
  5. Now add the Bundle case to price. The hard part is to match the varargs. Use case Bundle(_, disc, its @ _*). You want to recursively compute the sum of the prices of the items (Hint: map, sum), and subtract the discount.
  6. Suppose you have a bundle made up of an article and another bundle, like this:
    val special = Bundle("Father's day special", 20.0,
      Article("Scala for the Impatient", 39.95),
      Bundle("Anchor Distillery Sampler", 10.0,
        Article("Old Potrero Straight Rye Whiskey", 79.95),
        Article("Junípero Gin", 32.95)))
    
    Write a one-line assigment that extracts the description and price of the first article:
    val ...(descr, price)... = special

Part 3: The Option Type

  1. In this part, we will reimplement the Option type for Double values. Provide classes DoubleOption, SomeDouble, and NoDouble. What are they?
  2. Write a function inv that maps x into its inverse (1 / x), returning a DoubleOption. Return NoDouble when x is zero. What is your function? What happens when you call inv(2)? inv(0)?
  3. Write a function that composes two functions of type Double => DoubleOption, yielding another function of the same type. The composition should yield NoDouble if either function does. For example, with
    def f(x: Double) = if (x <= 1) SomeDouble(sqrt(1 - x)) else NoDouble
    val h = compose(inv, f) // inv(f(x)) when defined
    
    the result of h(1) and h(2) are NoDouble, but h(0) is SomeDouble(1).
  4. Define a method isEmpty that returns true for NoDouble and false for SomeDouble. Use pattern matching.
  5. Define a method get that returns the value wrapped in SomeDouble and throws a NoSuchElementException if there is no value. Use pattern matching.
  6. Repeat the previous two steps, using polymorphism. Define abstract methods in DoubleOption and override them in SomeDouble and NoDouble.