Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MyList - step 5

Combine and expand lists with concatenation and flatMap. These two operations unlock expressive transformations and composition.

  • ++ (concatenation) glues two lists together.
  • flatMap maps each element to a list and flattens the results.

Exercise: add ++ and flatMap

trait MyList[A]:
  ...
  infix def ++(other: MyList[A]): MyList[A]
  def flatMap[B](f: A => MyList[B]): MyList[B]

Hints:

  • For ++, think in terms of rebuilding the left list while pointing the last node to the right list.
  • For flatMap, use ++ to combine the list produced from the head with the recursively flatMapped tail.

Solution: concatenation (++)

Empty

override infix def ++(other: MyList[A]): MyList[A] = other

Appending anything to an empty list returns the other list unchanged.

Cons

override infix def ++(other: MyList[A]): MyList[A] =
  Cons(head, tail ++ other)

Rebuild the left list node by node, and when you reach the end (Empty), hook it up to other.

Walk-through:

[1,2,3] ++ [4,5,6]
Cons(1, [2,3] ++ [4,5,6])
Cons(1, Cons(2, [3] ++ [4,5,6]))
Cons(1, Cons(2, Cons(3, [] ++ [4,5,6])))
Cons(1, Cons(2, Cons(3, [4,5,6])))
[1,2,3,4,5,6]

Solution: flatMap

Empty

override def flatMap[B](f: A => MyList[B]): MyList[B] = Empty[B]()

Flat-mapping an empty list yields an empty list (regardless of f).

Cons

override def flatMap[B](f: A => MyList[B]): MyList[B] =
  f(head) ++ tail.flatMap(f)
  • Transform head into a list with f(head).
  • Concatenate it with the flat-mapped tail.

Walk-through:

[1,2,3].flatMap(a => [a, a + 1])
[1,2] ++ [2,3].flatMap(f)
[1,2] ++ [2,3] ++ [3].flatMap(f)
[1,2] ++ [2,3] ++ [3,4] ++ [].flatMap(f)
[1,2] ++ [2,3] ++ [3,4] ++ []
[1,2,2,3,3,4]

Try it out

@main
def main(): Unit =
  val myList: MyList[Int] = MyList(1, 2, 3)
  val otherList: MyList[Int] = MyList(4, 5)

  println(myList ++ otherList)                      // MyList(1, 2, 3, 4, 5)
  println(myList.flatMap(a => MyList(a, a + 1)))    // MyList(1, 2, 2, 3, 3, 4)

Key ideas

  • ++ structurally rebuilds the left list and attaches the right list at the end.
  • flatMap is “map then flatten,” and becomes simple once ++ is available.