Lambda 类型推断和隐式转换

Lambda type inference and implicit conversions

我定义了以下 class:

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

这行得通。但是,它会导致类型推断出现问题。

例如,如果我尝试调用一个名为 "map" 的方法,该方法采用 TransparentFunction1[Int, Int] 类型的参数,如下所示:

map(_ + 2)

我收到错误 "missing parameter type for expanded function",而如果 map 的参数类型只是 Int => Int,则类型推断工作正常。

有没有办法修复宏以便类型推断继续工作?

我认为没有办法做到这一点。前段时间,我曾尝试对 Scala.js 中的 js.FunctionN 做同样的事情(例如,将 T1 => R 隐式转换为 js.Function1[T1, R]),但我永远做不到使类型推断适用于 lambda 的参数。好像是不可能的,可惜。

要解决此问题,您只需要 TransparentFunction1 扩展 Function1(这看起来很自然,因为 TransparentFunction1 在概念上非常类似于 Function1,它只是添加自定义 toString 但应作为普通函数使用):

class TransparentFunction1[-T1, +R1](val func : T1 => R1, val text : String) extends (T1 => R1){
  @inline
  def apply(t : T1) = func(t)

  override def toString = text
}

对于定义一个 扩展 Function1 的类似 class 的函数,我只能看到少数几个原因。在我的脑海中,主要原因是当你的 class 被设计为用作隐式值(例如类型 class),并且你不希望编译器自动使用那些隐式值作为隐式转换(如果它扩展 Function1,它就会这样做)。这里似乎不是这种情况,因此 TransparentFunction1 扩展 Function1 似乎是正确的做法。