Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: understanding parametric polymorphism

What is the difference between

def drop1[A](l: List[A]) = l.tail

and

def drop1(l: List[Int]) = l.tail

provided the usage looks something like

drop1(List(1,2,3))

?

When should one or the other be used and why? Whereas I can understand the second example, I don't really understand the purpose of the first one.

like image 536
Caballero Avatar asked Dec 26 '22 15:12

Caballero


2 Answers

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
like image 143
flavian Avatar answered Feb 04 '23 23:02

flavian


In short -- some operations do not depend on a specific type and can be abstracted. Counting apples and counting oranges is essentially the same operation. If you're going to reuse algorithm, it is way smarter to abstract some types away instead of writing

def countOranges(xs: List[Orange]) = { some code } 
def countApples(xs: List[Apple]) = { the very same code }
like image 42
om-nom-nom Avatar answered Feb 05 '23 01:02

om-nom-nom