scalascala-macrostype-bounds

How to require at compile time that a type parameter be a trait (and not a class or other type value)?


I'm looking for some kind of upper bound on a generic parameter T that ensures that T is a trait.

class Foo
trait Bar

def f[A ??? IsATrait] = ???

// f[Foo] Won't compile
f[Bar] // this is fine

Solution

  • Try

    typeOf[Bar].typeSymbol.asClass.isTrait // true
    typeOf[Foo].typeSymbol.asClass.isTrait // false
    

    At compile time

    import scala.language.experimental.macros
    import scala.reflect.macros.whitebox
    
    trait IsATrait[A]
    
    object IsATrait { 
      implicit def materialize[A]: IsATrait[A] = macro impl[A]
    
      def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
        import c.universe._
        val tpA = weakTypeOf[A]
        if (tpA.typeSymbol.asClass.isTrait)
          q"new IsATrait[$tpA] {}"
        else c.abort(c.enclosingPosition, s"$tpA is not a trait")
      }
    }
    
    def f[A: IsATrait] = ???
    
    f[Bar] // compiles
    
    f[Foo]
    //Information: IsATrait.materialize is not a valid implicit value for IsATrait[Foo] because:
    //             hasMatchingSymbol reported error: Foo is not a trait
    //
    //Error: could not find implicit value for evidence parameter of type IsATrait[Foo]
    //
    //Error: not enough arguments for method f: (implicit evidence$1: IsATrait[Foo])Nothing.
    //       Unspecified value parameter evidence$1.