Search code examples
scalatype-erasurecontext-bound

Shorthand for defining scala context bound in trait


In scala abstract class, if you want to define a context bound, you can simply use, e.g. [T: ClassTag] in parameter, however this is not possible in trait:

trait Foo[T: ClassTag]

Error:(11, 35) traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'
trait Foo[T: ClassTag]
         ^

if you define:

trait Foo[T] {

  implicit def ctg: ClassTag[T] = implicitly[ClassTag[T]]
}

object Bar extends Foo[Int]

then any attempt to read ctg inside Bar will trigger a StackOverflowError, as the implicit parameter becomes tail-recursive.

So what's the best way to allow ctg to be defined in a trait that automatically expose subclasses to context bound?


Solution

  • There isn't a nice way. A context bound is short-hand for an implicit parameter, and traits do not have parameters. That is, when you write:

     class Foo[T : ClasTag]
    

    The compiler de-sugars your code to:

     class Foo[T](implicit ev: ClassTag[T])
    

    This is of course not possible with a trait. If you must work around this with a trait, you can make the ClassTag abstract, and force the class that extends it to implement it:

    trait Foo[T] {
      implicit def ctg: ClassTag[T]
    }
    
    object Bar extends Foo[Int] {
      implicit val ctg = classTag[Int]
    }
    

    This looks slightly better with a class in the middle, so that you don't need to specify Int twice when defining Bar:

    trait Foo[T] {
      implicit def ctg: ClassTag[T]
    }
    
    class FooImpl[T](implicit val ctg: ClassTag[T]) extends Foo[T]
    
    object Bar extends FooImpl[Int]