A possible approach to solve this problem is to create a simple type class that would be only available for functions:
trait Functionable[T]
With implicit macro that materializes implicit values:
object Functionable {
implicit def materialize[T]: Functionable[T] = macro materialize_impl[T]
def materialize_impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[Functionable[T]] = {
val funcs = (0 to 22).map { c.universe.definitions.FunctionClass(_) }.toList
if (funcs.exists { ttag.tpe.typeSymbol == _ })
c.universe.reify { new Functionable[T] { } }
else {
c.abort(c.macroApplication.pos, "not a function")
}
}
}
This particular implementation won't work for subtypes but you can customize it if you need more.
And then we can define a macro as:
def myMacro[T](f: T)(implicit functionable: Functionable[T]) = macro myMacroImpl[T]
def myMacroImpl[T](c: Ctx)(f: c.Expr[T])(functionable: c.Expr[Functionable[T]])(implicit ttag: c.WeakTypeTag[T]): c.Expr[String] = c.literal(ttag.tpe.toString)
This macro will only be expanded for functions and will fail with compile-time implicit not found error if argument is not a function. From withing a macro you can reflectively get the info about types through weak type tag of the function type.