Scala: Why does function need type variable in front?

157 views Asked by At

From working on the first problem of the 99 Scala Puzzles I defined my own version of last like so:

def last[A](xs: List[A]): A = xs match {
  case x :: Nil => x
  case x :: xs => last(xs)
}

My question is: Why is it necessary for last to be directly followed by the type variable, as in last[A]? Why couldn't the compiler just do the right thing if I wrote the function like so:

def last(xs: List[A]): A
    .....

(leaving the [A] off the end of last[A]?)

And if the compiler is capable of figuring it out, then what is the rationale for designing the language this way?

3

There are 3 answers

0
DJG On BEST ANSWER

I got an insight from @Lee's comment:

How would the compiler know that the A in List[A] doesn't refer to an actual type called A?

To demonstrate to myself that this made sense, I tried substituting the type variable A, with the name of an actual type String, and then passed the function a List[Int], seeing that when last is declared like def last[String](xs: List[String]): String, I was able to pass last a List[Int]:

scala> def last[String](xs: List[String]): String = xs match {
     | case x :: Nil => x
     | case x :: xs => last(xs)
     | }
last: [String](xs: List[String])String

scala> last(List(1,2,3,4))
res7: Int = 4

Therefore proving the identifier String does behave like a type variable, and does not reference the concrete type String.

It would also make debugging more difficult if the compiler just assumed that any identifier not in scope was a type variable. It therefore, makes sense to have to declare it at the beginning of the function definition.

0
Idan Arye On

A appears 3 times:

  1. last[A]

  2. List[A]

  3. : A(after the argument list)

The 2nd one is needed to specify that the List contains objects of type A. The 3rd one is needed to specify that the function returns an object of type A.

The 1st one is where you actually declare A, so it could be used in the other two places.

0
vptheron On

You need to write last[A] because A does not exist. Since it does not exist, by declaring it after the name of the function you actually get a chance to define some expectations or constraints for this type.

For example: last[A <: Int] to enforce the fact that A has to be a subtype of Int

Once it's declared, you can use it to define the type of your parameters and your return type.