Bruno Oliveira Bruno Oliveira - 3 months ago 11
Scala Question

Functional Programming exercise with Scala

I have recently started reading the book Functional Programming in Scala by Paul Chiusano and RĂșnar Bjarnason, as a means to learn FP. I want to learn it because it will open my head a bit, twist my way of thinking and also hopefully make me a better programmer overall, or so I hope.

In their book, Chp. 3, they define a basic singly-linked-list type as follows:

package fpinscala.datastructures

sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]

object List {
def sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x,xs) => x + sum(xs)
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x,xs) => x * product(xs)
}
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}


I'm now working on implementing the tail method, which shall work similarly to the tail method defined in Scala libraries. I guess that the idea here, is to define a tail method inside the List object, what they call a companion method, and then call it normally in another file (like a Main file).

So far, I have this:

def tail[A](ls: List[A]): List[A] = ls match {
case Nil => Nil
case Cons(x,xs) => xs
}


Then I created a Main file in another folder:

package fpinscala.datastructures

object Main {
def main(args:Array[String]):Unit = {
println("Hello, Scala !! ")
val example = Cons(1, Cons(2, Cons(3, Nil)))
val example2 = List(1,2,3)
val example3 = Nil
val total = List.tail(example)
val total2 = List.tail(example3)
println(total2)
}
}


This works and gives me:

Hello, Scala !!
Cons(2,Cons(3,Nil))


My question is:

Is this the correct way to write the tail method, possibly, as the authors intended? And is this package structure correct? Because it feels very wrong to me, although I just followed the authors package.

I also don't know if I should have used a specific type instead of writing a polymorphic method (is this the name?)...

Bear with me, for I am a newbie in the art of FP.

Answer

In the default Scala list implementation, attempting to take the tail of an empty list would throw an UnsupportedOperationException. So you might want something more like

def tail[A](ls: List[A]): List[A] = ls match {
    case Nil => throw new UnsupportedOperationException()
    case Cons(x,xs) => xs
}

Also, qantik's answer where he suggests using the :: operator would work with Scala's default list implementation, but since there isn't a :: method defined on this custom list implementation it won't work.

Finally, you may want to consider defining tail so that instead of doing

val list = List(1, 2, 3)
val restOfList = tail(list). 

you could instead do

val list = List(1, 2, 3)
val restOfList = list.tail

That would require defining the method on the List trait, as opposed to in the List object.