// 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