The Road to Functional Programming

Educational material to learn functional programming in Scala, from scratch, in a structured and comprehensive way.

View project on GitHub

Proposed solutions for exercises

Chapter 6 - Advanced functions concepts

Exercises 6.1

6.1.1

Implements the functions with the following definitions. Note that there might be any number of implementation, none, one or more (though not too many):

// f
def f1[A](a: A, b: A): A = a
def f1[A](a: A, b: A): A = b

// g
def g[A, B](a: A, b: B): A = a

// h
def h[A, B](a: A, b: B): B = b

6.1.2

Implement the functions with the following definitions:

// f
def f[A, B](as: List[A]): List[B] = Nil

// g
def g1[A, B](as: Option[A]): Option[B] = None
def g2[A, B](as: Option[A]): Option[B] = as

// h
def h1[A](as: List[A]): List[A] = Nil
def h2[A](as: List[A]): List[A] = as
def h3[A](as: List[A]): List[A] = {
  if (as.isEmpty) Nil else as.head :: Nil
}
def h4[A](as: List[A]): List[A] = {
  if (as.isEmpty) Nil else as.tail
}

// i
def i1[A, B](as: List[A], f: A => B): List[B] = Nil
def i2[A, B](as: List[A], f: A => B): List[B] = {
  if (as.isEmpty) Nil else f(as.head) :: Nil
}
def i3[A, B](as: List[A], f: A => B): List[B] = {
  if (as.isEmpty) Nil else f(as.head) :: h3(as.tail, f)
}

How many implementations did you find? Why is that?

The signatures g, h and i have multiple implementations and this number is bounded by the number of different input values we have. For Option we only have 2 implementations and for List the number is bounded by the elements of the list.

This happens because we have lost parametricity in favour of using Option and List.

Exercises 6.2

6.2.1

For each of the following questions tell the domain and codomain of the function, if the function is total or partial and if it is surjective, injective or bijective:

6.2.1.1

A function String => String that associate each string with their reverse (e.g. “Scala”->”alacS”)

def reverse: String => String = { _.reverse }
def reverseInverse: String => String = reverse

reverse("ABC")
reverseInverse("CBA")
reverseInverse(reverse("ABC")) == "ABC"

// Output:
//   String = "CBA"
//   String = "ABC"
//   Boolean = true

The function domain is the String set and its codomain is the entire String set.

It is total because it works for any string.

It is surjective because it uses all elements of its codomain.

It is injective because for each input string it always produce a different output string.

Because it’s surjective and injective it is also bijective. It’s inverse function is itself.

6.2.1.2

A function Int => Int that associates negative numbers with their positive value;

def abs: Int => Int = { input =>
  if (input < 0) -input else input
}

abs(3)
abs(0)
abs(-2)

// Output:
//   Int = 3
//   Int = 0
//   Int = 2

The function domain is the Int set and its codomain is the positive subset of the Int set.

It is total because it works for any number.

It is not surjective because it uses only positive numbers.

It is not injective because there are two input elements associated to each output element (e.g. both -4 and 4 in the input are associated with 4 in the output)

The function is not bijective.

6.2.1.3

A function Int => Int that associates even numbers into their double;

def doubleEven: PartialFunction[Int, Int] = {
  case even if even % 2 == 0 => even * 2
}

doubleEven(0)
doubleEven(2)
doubleEven(1)

// Output:
//   res10: Int = 0
//   res11: Int = 4
//   scala.MatchError: 1 (of class java.lang.Integer)

The function domain is the even subset of the Int set and is a subset of the Int set.

It is partial because it is not defined for odd inputs.

It is not surjective because it uses only even numbers.

It is injective because each distinct input is associated with a different output value.

The function is not bijective.

6.2.2

Given a case class case class Person(name: String, surname: String) create a function Person => (String, String) that associates an instance of Person to a tuple that contains name and surname. Is this function a bijection? If yes write also its inverse.

case class Person(name: String, surname: String)

def toTuple(person: Person): (String, String) = {
  (person.name, person.surname)
}

def fromTuple(input: (String, String)): Person = {
  Person(input._1, input._2)
}

val author = Person("Matt", "Smith")
val tupled = toTuple(author)
var backAgain = fromTuple(tupled)

backAgain == author

// Output
//   author: Person = Person("Matt", "Smith")
//   tupled: (String, String) = ("Matt", "Smith")
//   backAgain: Person = Person("Matt", "Smith")
//   Boolean = true

Yes, the function is bijective and you can write its inverse.