я определил следующий класс:Lambda умозаключение типа и неявное преобразование
class TransparentFunction1[-T1, +R1](val func : T1 => R1, val text : String) {
@inline
def apply(t : T1) = func(t)
override def toString = text
}
В принципе, TransparentFunction1
просто обертка Function1
, которая обеспечивает текстовое поле удобочитаемого, описывающее то, что функция.
Я хотел бы определить неявное преобразование, которое может преобразовать любой Function1 в TransparentFunction1
, передавая код функции в текстовый параметр.
Я определил такое неявное преобразование с помощью макроса:
implicit def transparentFunction1[T1, R1](expression : T1 => R1) : TransparentFunction1[T1, R1] = macro Macros.transparentImpl[T1, R1, TransparentFunction1[T1, R1]]
object Macros {
def transparentImpl[T : context.WeakTypeTag, U : context.WeakTypeTag, V : context.WeakTypeTag](context : scala.reflect.macros.whitebox.Context) (expression : context.Expr[T => U]) : context.Expr[V] = {
import context.universe._
context.Expr[V](
Apply(
Select(
New(
TypeTree(
appliedType(weakTypeOf[V].typeConstructor, weakTypeOf[T] :: weakTypeOf[U] :: Nil)
)
),
termNames.CONSTRUCTOR
),
List(expression.tree, Literal(Constant(expression.tree.toString)))
)
)
}
}
Это работает. Однако это вызывает проблему вывода типа.
Например, если я пытаюсь вызвать метод, названный «карта», которая принимает аргумент типа TransparentFunction1[Int, Int]
так:
map(_ + 2)
я получаю ошибку «отсутствует параметр типа для расширенной функции», а если Тип параметра карты был только Int => Int
, вывод типа работает правильно.
Есть ли способ исправить макрос, чтобы тип вывода продолжал работать?
(пахнет лбом) Не знаю, почему я об этом не думал. Предварительно это, кажется, устраняет проблему. Я должен экспериментировать с макросом, чтобы быть уверенным. – Nimrand
Ow ... это ... очень удивительно. Нет никакой причины, по которой вывод типа должен иметь возможность выводить типы в этом случае, но не в пример OP. Это похоже на артефакт реализации ^^ Но хорошая новость для вас, а затем :) – sjrd
Это действительно имеет смысл. Когда компилятор видит лямбду, он смотрит на тип параметра метода. Поскольку ожидаемый тип параметра является своего рода Function1 [T, R], он может знать из аргумента первого типа T, что параметр _ имеет тип Int. Если типы не были связаны друг с другом, у него нет способа вывести тип лямбды до тех пор, пока не будет применено неявное преобразование. Но он не может найти неявное преобразование, потому что он не знает, с какого типа его преобразование. Курица и яйцо. Алгоритм вывода, вероятно, можно было бы скорректировать для размещения, но это было бы сложно. – Nimrand