2

Why Scala compiler cannot compile next code :

trait Profile {}
class SomeProfile extends Profile

trait Foo {
  def get[T <: Profile]: Option[T]
}

object Example {
  val foo: Foo = new Foo {
    // This works (but might give runtime exception), but it is not ugly? :)
    def get[T <: Profile]: Option[T] = Some((new SomeProfile).asInstanceOf[T])
  }

  val foo2: Foo = new Foo {
    // This does not compile with type mismatch :(
    def get[T <: Profile]: Option[T] = Some(new SomeProfile)
  }
}

Compiler says:

type mismatch;
 found   : Playground.this.SomeProfile
 required: T

But SomeProfile is T, no?

Update:

I want to implement this trait DatabaseConfigProvider with exact type and do it in this way:

val dc: DatabaseConfig[JdbcProfile] = ???
val prov = new DatabaseConfigProvider {
  def get[P <: BasicProfile] = dc.asInstanceOf[DatabaseConfig[P]]
}

which looks ugly because of asInstanceOf.

Community
  • 1
  • 1
Artavazd Balayan
  • 2,353
  • 1
  • 16
  • 25

2 Answers2

2

You wrongly declared input argument. Try below:

trait Profile {}
class SomeProfile() extends Profile

trait Foo {
  def get[T >: Profile]: Option[T]
}

object Example {
  val foo2: Foo = new Foo {
    override def get[T >: Profile]: Option[T] = Some(new SomeProfile())
  }
}

Explanation for what :> does, you could find easily in Stackoverflow (e.g.: What does [B >: A] do in Scala?)

tmucha
  • 689
  • 1
  • 4
  • 19
  • 1
    There is no mistake with input argument declaration, I wanted to have `<:` => an upper type bound, – Artavazd Balayan Feb 28 '18 at 08:48
  • How you want to use then your get method. If you want declare as ```val e:Option[Profile] = foo2.get```(with compilation error). – tmucha Feb 28 '18 at 10:12
1

Output type of your method get is defined by caller. You added type bounds (as T <: Profile) but this does only mean limitations for caller. Any casts (as you did) will fail at runtime if caller ask for another subtype of Profile than one you casted.

If you provide more details on what you expect to get as result, I can extend answer with specific advise how to get it.

Evgeny
  • 1,760
  • 10
  • 11
  • I checked implementation and came to SlickApi. And as I see there `asInstanceOf` is actively used. So, suppose that you do not have another choice. The only thing is that there are said that `Usually, you shouldn't need to create instances of `DatabaseConfigProvider` explicitly. Rather, you should rely on dependency injection.` But if you need, looks like you do not have other choice except of `asInstanceOf` – Evgeny Feb 28 '18 at 09:22
  • I need it for integration test where I can define configuration to the database in a code, not in a configuration file. I'm just curious why the compiler is not able to figure it out? – Artavazd Balayan Feb 28 '18 at 09:25
  • 1
    Figure out what? Output type is generic. Without cast you are trying to return specific type, which is wrong. In other words for method which returns *any child of Profile* (depends on call context), you return specific type. – Evgeny Feb 28 '18 at 09:30
  • Is not this constraint `T <: Profile` (must be a subtype of `Profile` ) respected? Because `SomeProfile` is subtype (implementation) of `Profile` (`class SomeProfile extends Profile`)? – Artavazd Balayan Feb 28 '18 at 09:33
  • 1
    No. Type bound specify bounds for caller. So, any call by caller as `get[AnyProfileChild]` will be valid. And you return one specific type. – Evgeny Feb 28 '18 at 09:36