2013-12-03 3 views
2

Я пытаюсь написать макрос, который работает с параметром функции. Я обрабатываются анонимные функции всех длин аргументов довольно легко, сделав параметр в макро Any, а затем на соответствие Function(body, params), но я хотел бы, чтобы иметь возможность передать ссылку на функцию переменной:Как определить, является ли параметр макроса Scala функцией?

val valFunction = (something: Int) => (something * 3).toString 
val functionVal = Macros.myMacro(valFunction) 

I был в состоянии использовать WeakTypeTag сек согласно official documentation, чтобы сделать эту работу с функцией одного параметра:

def myMacro[T, U](param: T => U) = macro myMacroImpl[T, U] 

def myMacroImpl[T, U](c: Context)(param: c.Expr[T => U]) 
    (implicit tt: c.WeakTypeTag[T], implicit ut: c.WeakTypeTag[T]) = {...} 

но макрос работает только для функций одного параметра. Я попробовал проверить член с именем «apply», который также был методом, но я столкнулся с проблемами стирания, а также был обеспокоен перегруженными методами и таковыми. Обходной путь, к которому я в настоящее время изобилую, - создать 23 макроса для Function0 через Function22, но whoo-boy Я не доволен этим. Есть ли другой подход, который я могу предпринять?

ответ

1

Возможный подход к решению этой проблемы заключается в создании простого типа класса, который будет доступен только для функций:

trait Functionable[T] 

С неявной макрос, который материализует неявные значения:

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") 
    } 
    } 
} 

Эта конкретная реализация не будет работать для подтипов, но вы можете ее настроить, если вам нужно больше.

И тогда мы можем определить макрос:

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) 

Этот макрос будет расширяться только для функций и подведет с компиляции неявной не найдена ошибка, если аргумент не является функцией. С помощью макроса вы можете получить информацию о типах через тег слабого типа типа функции.

Whole example can be found here.

+0

'c.universe.definitions.FunctionClass' был именно тем, что я искал! Неявный класс типов интересен, но я пошел только с совпадением тега типа с использованием FunctionClass и прервал макрос в случае не-макета. Благодаря! –

+0

Некоторые незначительные улучшения: 1) в силу @implicitNotFound вы можете предоставить пользователям сообщение об ошибке компиляции, 2) я бы, вероятно, сделал Functionable классом, так что вам не нужно создавать новый анонимный подкласс каждый раз при материализации происходит –

2

Вы, вероятно, придется принять Any (или несвязанный родовое) в качестве макро-типа параметра, а затем вручную проверить внутри макроса, что аргумент на самом деле один из Function0 в Function22. Это не должно быть, что утомительно, хотя:

val functionTypes = for(i <- 0 to 22) 
    yield c.mirror.staticClass(s"scala.Function$i").toType.erasure 

Теперь мои Tree «ы типа один из вышеперечисленного?

functionTypes.exists(funTpe => tree.tpe <:< funTpe) 
Смежные вопросы