scalagenericstype-parameterhigher-kinded-types

Type Arguments and Bounds in Scala


I have the following example:

import scala.concurrent.Future

trait MyTrait[F[_]] {

  case class Test[X[_]](x: X[Int])

  def test[G[_]]: F[Test[G]]

}
class LocImpl extends MyTrait[Future] {

  import scala.concurrent.ExecutionContext.Implicits.global

  def test[Option]: Future[Test[Option]] = {
    Future { new Test[Option](Option(1)) }
  }
}

Which fails compilation due to the reason that:

Type argument Option does not have the same kind as its bound [_$2]

I'm binding the generic type on the test function to Option and binding the trait to Future. So what is the problem here?

https://scastie.scala-lang.org/35pqGtqnQIGvZpGl4BTlFg


Solution

  • For the reference, the beginning was here: Scala Higher Kinded Types for Traits and Method Parameters

    Option in def func[Option]... is not scala.Option, you're just defining a new type parameter calling it Option, it's the same as def func[A]..., you just called A Option, which shadows scala.Option.

    This is a difference def func[A] (definition site) vs. func[A] (call site) i.e. whether A is a new type or known type.

    It's better to use scalacOptions += "-Xlint:type-parameter-shadow" to avoid shadowing known types.

    What causes this type error in a pattern guard for matching list of tuples

    Strange Error with String in Scala 2.12.7

    Type parameters applied to Scala Function

    And since def test[Option]... is the same as def test[A]..., errors are understandable. Test[Option] aka Test[A] doesn't make sense because of a disagreement in kind. Option(1) aka A(1) doesn't make sense either.

    So it should be just

    def test: Future[Test[Option]] = {
      Future { new Test[Option](Option(1)) }
    }
    

    without type parameter. But then you break overriding. You seem to want having G an abstract type (to be implemented in inheritors) rather than method's type parameter (when the method must be defined for arbitrary G)

    trait MyTrait[F[_]] {
    
      case class Test[X[_]](x: X[Int])
    
      type G[_]
    
      def test: F[Test[G]]
    
    }
    class LocImpl extends MyTrait[Future] {
    
      import scala.concurrent.ExecutionContext.Implicits.global
    
      type G[A] = Option[A]
    
      def test: Future[Test[Option]] = {
        Future { new Test[Option](Option(1)) }
      }
    }
    

    https://scastie.scala-lang.org/DmytroMitin/rk82W02DQOiFAJ7mghHcAQ/1

    It's similar to having G a type parameter of the type class

    trait MyTrait[F[_], G[_]] {
    
      case class Test[X[_]](x: X[Int])
    
      def test: F[Test[G]]
    
    }
    class LocImpl extends MyTrait[Future, Option] {
    
      import scala.concurrent.ExecutionContext.Implicits.global
    
      def test: Future[Test[Option]] = {
        Future { new Test[Option](Option(1)) }
      }
    }
    

    https://scastie.scala-lang.org/DmytroMitin/rk82W02DQOiFAJ7mghHcAQ/2