Search code examples
scalaimplicit-conversioncallbyname

Conversion to tuple with by-name parameter


I would like to create a function with the following signature:

def myFunction[T](functionWithName: (String, => T)): T 

so that I can call it, e.g., like this: val x = myFunction("abc" -> 4 * 3). Tuple doesn't accept by-name parameter, however, so the signature above is invalid.

Inspired by this answer, I tried the following implicit conversion:

implicit class ByName[T](getValue: => T) extends Proxy {
  def apply(): T = getValue
  def self = apply()
}

def myFunction[T](functionWithName: (String, ByName[T])): T = {
  // do something
  functionWithName._2()
}

The implicit doesn't work in this case, however (unlike in the linked answer).

  • Why does the implicit conversion to ByName doesn't work?
  • How can I achieve the desired effect of calling myFunction("abc" -> 4 * 3) where 4 * 3 is passed by name?

Solution

  • I have two proposals to implement this:

    • Make whole tuple by-name: functionWithName: => (String, T)

    • Make your own class:

      class MyTuple[A, B](a: A, b: => B) {
        def _1: A = a
        def _2: B = b
        def toTuple = a -> b // optional
      }
      

    and have a custom implicit method somewhere, like -> for Tuple2 (see ArrowAssoc in Predef):

      implicit final class ArrAssoc[A](private val self: A) extends AnyVal {
        @inline def -->[B](y: => B): MyTuple[A, B] = {
          new MyTuple(self, y)
        }
      }
    

    Then you could say this:

      val t = 1 --> { println("blah"); 5 }
      //t._2
      //t.toTuple
    

    The b parameter should not be evaluated until you call t._2 Or even make toTuple imlicit conversion, so when Tuple2 is espected you can pass a MyTuple...