Lambda inference and implicit conversions

I have defined the following class:

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

  override def toString = text
}

      

Basically, TransparentFunction1

it's just a wrapper around Function1

it that provides a human-readable text field describing what the function is.

I would like to define an implicit conversion that can convert any Function1 to TransparentFunction1

by passing the function code to a text parameter.

I have defined such an implicit conversion using a macro:

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

      

It works. However, this causes a type inference problem.

For example, if I try to call a method named "map" that takes a type argument like TransparentFunction1[Int, Int]

this:

map(_ + 2)

      

I get the error "Missing parameter type for extended function", whereas if the type of the map parameter was simple Int => Int

, the type inference works correctly.

Is there a way to fix the macro to continue type inference?

+3


source to share


2 answers


To fix this, you need to have TransparentFunction1

extend Function1

(which seems natural anyway, given that it is TransparentFunction1

conceptually very similar to it Function1

, it just adds a custom one toString

, but should act differently like a simple funciton):

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
}

      



There are only a few reasons I can see for defining a functionally like class that does not extend Function1

. From my point of view, the main reason is that your class is meant to be used as implicit values ​​(like a type) and you don't want the compiler to automatically use those implicit values ​​as implicit conversions (which it will if it continues Function1

). It doesn't seem to be the case, so having TransparentFunction1

extend Function1

seems to be correct.

+2


source


I don't think there is a way to do this. A while ago I tried to do the same thing for js.FunctionN

in Scala.js (for example, implicitly convert a T1 => R

to js.Function1[T1, R]

) and I could never do a type inference operation for lambda parameters. It seems impossible, unfortunately.



+2


source







All Articles