Scala Generics, какая из этих 4 универсальных версий является конкретной для конкретной проблемы - PullRequest
0 голосов
/ 18 ноября 2018

Эти четыре версии компилируются, но мне любопытно, в каком контексте мы предпочитаем один вариант, а не другой.

// 1
def f[N, S <: Seq[N]](s: S)
// 2
def f[N, S[N] <: Seq[N]](s: S[N])

Они очень похожи при использовании 1 вместо 2

2 предполагает, что S имеет N в качестве общего параметра как 1, но тогда в чем разница между этими двумя?

Тогда у нас есть более общие настройки.

// 3
def f[N, S[X] <: Seq[X]](s: S[N])
// 3.a
def f[N, S[X] <: Seq[X]](s: S[N]): S[Int]

Из того, что я смиренно понял 3 разрешить извлекать универсальный тип контейнера, чтобы использовать его позже и получить что-то вроде 3.a.

Но в чем смысл необъявленного универсального параметра X, я полагаю, что это способ объявить что-то особенное, но я не понимаю.

// 4
def f[N, S[X] <: Seq[_]](s: S[N])

Я не знаю, что сказать о 4, кроме того, что я знаю Seq[_] означает Seq[Any]

Наконец, мне просто любопытно получить больше информации об этих инструментах и ​​их специфике, чтобы сделать вещи более правильно.

1 Ответ

0 голосов
/ 18 ноября 2018
// 2
def f[N, S[N] <: Seq[N]](s: S[N])

Идея состоит в том, что первый параметр N и N, упомянутые в S[N] <: Seq[N], являются полностью независимыми параметрами.Они просто имеют одно и то же имя.

N, упомянутый в S[N], виден только в пределах его границы <: Seq[N].N используется в определении параметра (s: S[N]) происходит от первого N, так как это единственный параметр N, видимый для определения типа параметра.Таким образом, вместо N в S[N] <: Seq[N] вы можете использовать любую букву, и это никак не повлияет на ваш тип параметра.

// 4
def f[N, S[X] <: Seq[_]](s: S[N])

Здесь вы только что проигнорировали параметр X.

Редактировать: как @ alexey-romanov, упомянутый в комментарии.Существует разница между S[X] <: Seq[X] и S[X] <: Seq[_]

Вот пример, показывающий разницу:

def f1[N, S[X] <: Seq[X]](s: S[N]) = ""

def f2[N, S[X] <: Seq[_]](s: S[N]) = ""

type Foo[A] = Seq[Int]

val foo: Foo[String] = Seq(2,3)

//f1(foo) -- compilation error
f2(foo)

Проблема здесь в том, что constrictor типа является своего рода «функцией над типами»мы можем определить такую ​​«функцию», которая принимает один тип в качестве параметра, но возвращает тип, параметризованный другим параметром, не связанным с параметром, используемым в конструкторе типов.(См. Тип Foo)

Передача foo val в f2 - это нормально, поскольку X выводится в String, а Foo[String] является «подтипом» (на самом деле они равны) Seq[Int], но когда мы передаем foo в f1, X по-прежнему String, но Foo[String] не является «подтипом» Seq[String] (потому что Foo [String] == Seq [Int] не подтипSeq [String])

// 1
def f[N, S <: Seq[N]](s: S)

И здесь вы сказали, что N, используемый в Seq[N], совпадает с первым параметром N.Так что это то же самое N

...