2014-01-14 4 views
3

В чем разница междуScala: понимание параметрического полиморфизма

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

и

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

при условии, что использование выглядит как

drop1(List(1,2,3)) 

?

Когда нужно использовать одно или другое и почему? В то время как я могу понять второй пример, я не совсем понимаю цель первого.

ответ

6

Это очень просто. Ваш первый пример относится к концепции generics.

Дженерик имеет простую цель, чтобы сделать определенные методы родового, например, не зависят от типа.

Давайте рассмотрим этот тривиальный пример. Скажем, я хочу написать метод drop1 для List.

Я могу либо написать один для каждого типа: (медленно, повторяющийся):

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 

Вы можете увидеть, как вы должны были бы написать выше для каждого отдельного типа. Чтобы преодолеть это, у вас есть дженерики:

def drop1[A](l: List[A]): List[A] = l.tail // this works for any given type. 

, который по существу говорит: Вне зависимости от типа, содержащиеся в списке, дают мне хвост. Вместо того, чтобы писать тысячи вариантов drop1 для практически бесконечного числа типов, мне нужно только написать один.

Сейчас в Scala, ваша реализация лучше всего сделать с:

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 

Это также вообще плохая идея переименовать хорошо известные методы библиотеки. Операция tail небезопасна, а drop безопасен. Все, что вы вызываете, - путаница, поскольку существует метод по умолчанию drop.

List(1, 2, 3) drop 1 
+0

Спасибо за ваш ответ. Почему именно первый '[A]' нужен в моем первом примере? Это просто требование синтаксиса? – Caballero

+2

Да, поэтому компилятор может различать тип, который вы вводите из некоторого уже определенного типа A. Почему? Чтобы вы не ошиблись и не делаете свое намерение явным. –

+0

Спасибо. По сути, во время чтения этого урока я поднял этот вопрос. – Caballero

3

Короче говоря, некоторые операции не зависят от конкретного типа и могут быть абстрагированы. Подсчет яблок и счетных апельсинов - это, по сути, одна и та же операция. Если вы собираетесь использовать алгоритм повторного использования, то лучше использовать абстрактные типы, а не писать

def countOranges(xs: List[Orange]) = { some code } 
def countApples(xs: List[Apple]) = { the very same code }