2

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
  • 5
    How would the compiler know that the A in List[A] doesn't refer to an actual type called A? Commented Dec 10, 2013 at 23:32
  • 1
    I suppose a more lenient compiler, seeing that there is no explicitly declared type A in scope, conceivably could introduce an implicit declaration at the most local scope that makes sense. Commented Dec 10, 2013 at 23:50
  • 1
    last is a method, not a function. Functions are declared like this: val length: List[_] => Int = xs => xs match { case Nil => 0; case x :: xs => 1 + length(xs) } Note: I don't think you can write last as a function since functions can't be polymorphic AFAIK. The closest you can get is val last: List[Any] => Any = xs => xs match { case x :: Nil => x; case x :: xs => last(xs) } which is practically useless because you lose any information about the element type. Commented Dec 11, 2013 at 11:56

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.