Ankit Khettry
Ankit Khettry

Reputation: 1027

Motivation behind type parameters with method definitions

I am new to scala. I have a class:

abstract class List[T] {

  def isEmpty: Boolean
  def head: T
  def tail: List[T]
  def nth[T](elem : T, index: Int) : Int =
      if (this.isEmpty) throw new IndexOutOfBoundsException("Element not found")
      else if (head.equals(elem)) return index
      else tail.nth(elem, index+1)
}

Regardless of the implementation, the method nth can clearly be written with or without the Type parameter [T], i.e. the declaration of the method without the Type parameter [T] in definition will be producing the same result as well:

def nth(elem : T, index: Int) : Int =
      if (this.isEmpty) throw new IndexOutOfBoundsException("Element not found")
      else if (head.equals(elem)) return index
      else tail.nth(elem, index+1)

From my understanding, there is no use of having type parameters with method definitions. If so, why was this feature added to the language? Can someone explain the very motivation behind this feature with a useful example? What kind of compile time checking is done in this case?

Update: Rest of the implementation of the Linked List:

class NilList[T] extends List[T]{

  def isEmpty:Boolean = true
  def head : Nothing = throw new NoSuchElementException("Nil node")
  def tail : Nothing = throw new NoSuchElementException("Nil node")
}

class ConsList[T] (val head: T, val tail: List[T]) extends List[T]{
  def isEmpty: Boolean = false
}

Upvotes: 2

Views: 128

Answers (3)

muhuk
muhuk

Reputation: 16095

l.nth("foo", 0) on a List[Int] would compile with the List below:

abstract class List[T] {
    def isEmpty: Boolean
    def head: T
    def tail: List[T]
    def nth[T](elem : T, index: Int) : Int =
        if (this.isEmpty) throw new IndexOutOfBoundsException("Element not found")
        else if (head.equals(elem)) return index
        else tail.nth(elem, index+1)
}

But it would give a compile time error if we defined List like below:

abstract class List[T] {
    def isEmpty: Boolean
    def head: T
    def tail: List[T]
    def nth(elem : T, index: Int) : Int =
        if (this.isEmpty) throw new IndexOutOfBoundsException("Element not found")
        else if (head.equals(elem)) return index
        else tail.nth(elem, index+1)
}

And the latter is the right way to write this code, Why would you want to call nth on a List[T], with an element type completely different than T?

I'm curious how a terminating list will be implemented using this abstract class though. Doesn't look like one of the better ways of (re)implementing linked lists.

Also consider covariance of T: abstract class List[+T].

Upvotes: 1

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149636

From my understanding, there is no use of having type parameters with method definitions.

There is no use of having type parameters on methods only if you're shadowing a class level type parameter. Shadowing a type parameter is usually a result of a programmer error, and the compiler is able to catch that and warn you that you're actually shadowing an existing type parameter.

Other than that, there are plenty of use cases where you want a generic type parameter and not on a class. Consider that you have an existing, invariant constraint on a type T, and you want a type V which has T as it's upper bound, but can also operate on derived classes. You'll do:

class Foo[T] {
    def playWithDerived[V <: T](param: V) = Unit {
        // Do stuff.
    }
}

And there are many other cases where this comes in as helpful.

Upvotes: 1

jwvh
jwvh

Reputation: 51271

Consider the following.

abstract class X[A] {
  def m1[A](arg: A)   // warning: A defined in method shadows A defined in class 
  def m2[B](arg: B)   // B can be unrelated to A
  def m3[C <: A](arg: C) // C must be subtype of A
  def m4[D >: A](arg: D) // D must be supertype of A
}

As you can see, method type parameters can serve many purposes.

Upvotes: 2

Related Questions