Scala Generics, which of these 4 generic version is specific to which particular problem

99 Views Asked by At

These four versions compile but i'm curious about context in which we should prefer one option rather than another.

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

They are pretty similar when use 1 rather than 2

2 impose that S have N as generic parameter as 1 but then what is the difference between these two ?

Then we have more general settings.

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

From what i humbly understood 3 authorize to extract the generic container type to reuse it later and get something like 3.a.

But what is the meaning of the undeclared X generic parameter, i suppose it's a way to declare something special but i don't get it.

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

I don't know what to say about 4 except than from what i know Seq[_] stands for Seq[Any]

Finally i'm simply curious to have more information about these tools and their specificity to get things done more properly.

1

There are 1 best solutions below

10
On BEST ANSWER
// 2
def f[N, S[N] <: Seq[N]](s: S[N])

The idea here is that the first N parameter and N mentioned in S[N] <: Seq[N] are completely independent parameters. They are just sharing the same name.

N mentioned in S[N] is visible only in a scope of its bound <: Seq[N]. N used in parameter definition (s: S[N]) comes from first N as this is the only N parameter visible for parameter type definition. So instead of N in S[N] <: Seq[N] you can use any letter and this will not affect your parameter type at any way.

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

Here you just ignored X parameter.

Edit: as @alexey-romanov mentioned in a comment. There is difference between S[X] <: Seq[X] and S[X] <: Seq[_]

Here is an example showing the difference:

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)

The problem here is as type constrictor is a kind of "function on types", we can define such "function" accepting one type as a parameter but returning type parametrized by another parameter not related to parameter used in type constructor. (See type Foo)

Passing foo val to f2 is fine because X is infered to String and Foo[String] is a "subtype"(actually they are equal) of Seq[Int], but when we pass foo to f1 the X is still a String but Foo[String] is not "subtype" of Seq[String] (because Foo[String]==Seq[Int] not subtype of Seq[String])

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

And here you said that N used in Seq[N] is the same as first parameter N. So this is the same N