It's very simple really. Your first example refers to the concept of generics.
Generics have a simple goal, to make certain methods generic, e.g non-type dependant.
Lets look at this trivial example. Say I want to write a drop1
method for List
.
I can either write one for every single type:(slow, repetitive):
def drop1(l: List[Int]): List[Int] = l.tail // this only works for Int
def drop1(l: List[String]): List[String] = l.tail // this only works for String
You can see how you'd have to write the above for every single type. To overcome this, you have generics:
def drop1[A](l: List[A]): List[A] = l.tail // this works for any given type.
Which essentially says: Whatever the type contained in the List is, give me the tail.
Instead of writing thousands of variants of drop1
for the virtually infinite number of types, I only need to write one.
Now in Scala, your implementation is best done with:
implicit class ListOps[A](val l: List[A]) extends AnyVal {
def drop1: List[A] = l match {
case head :: tail => tail
case Nil => Nil
}
}
// you can now have
List(1, 2, 3).drop1
It is also generally a bad idea to rename well known library methods. A tail
operation is unsafe and a drop
is safe. All you are causing is confusion, since there is a default drop
method.
List(1, 2, 3) drop 1