Squidly
Squidly

Reputation: 2717

Why does scalac not believe that a method does not fit the required type signature?

Why does this not compile?
The error given is class SomeElement needs to be abstract, since method eval in trait Element of type [T <: Typed]=> scala.util.Try[T] is not defined

I cannot understand why the method eval defined on SomeElement does not satisfy the type constraints.

As I understand it, eval should return something wrapped in a Try which subclasses Typed. Try is covariant in its type parameter. The implementation of eval in SomeElement returns a NumberLike, and NumberLike subclasses Typed. So what's gone wrong?

import scala.util.Try

trait Element {
   def eval[T <: Typed]: Try[T]
}

trait Typed

case class NumberLike(n: Long) extends Typed

case class SomeElement(n: Long) extends Element {
  def eval = Try {
    NumberLike(n)
  }
}

object app extends Application {
  println (SomeElement(5).eval)
}

Trying to add an explicit type parameter to eval in SomeElement doesn't help either:

case class SomeElement(n: Long) extends Element {
  def eval[NumberLike] = Try {
    NumberLike(n)
  }
}

Changing the defintion of SomeElement to the above gives:

  found   : <empty>.NumberLike
  required: NumberLike(in method eval)
    NumberLike(n)

EDIT I would really like to know why this doesn't compile. Workarounds for the problem are helpful, but I really want to know what's going on here.

Upvotes: 8

Views: 138

Answers (2)

cloud
cloud

Reputation: 1065

Since someone has given a solution, I'm trying to explain why.

In java, something like this is illegal:

class A {
  <T> T f() {return null;}
  Object f() {return null;}
}

But in scala, it's legal:

class A {
  def f[T](): T = ???
  def f(): Any = ???
}

The main difference is that: scala treat 2 methods with same name and parameters list different if one of them has type parameter and another don't.(I think the signature of scala method include type parameters, correct me if I was wrong :) )

So in your case, you can't overwrite a method with type parameter by a normal method.(they have different signatures)

I'm not sure which one is better, personally I prefer the scala rule :)

Upvotes: 0

cchantep
cchantep

Reputation: 9168

The type parameter T being defined on the function, not on the enclosing type Element, it can't be 'erased' by inheritance and must be kept on the overriding function (http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#class-members).

Moving the type parameter to Element definition make it work as following.

trait Element[T <: Typed] {
   def eval: Try[T]
}

trait Typed

case class NumberLike(n: Long) extends Typed

case class SomeElement(n: Long) extends Element[NumberLike] {
  def eval = Try {
    NumberLike(n)
  }
}

Or using a type member:

trait Element {
   type T <: Typed
   def eval: Try[T]
}

trait Typed

case class SomeElement(n: Long) extends Element {
  type T = NumberLike
  def eval = Try {
    NumberLike(n)
  }
}

Upvotes: 2

Related Questions