Search code examples
scalalambdadottyscala-3

Scala Implicit conversion for Lambdas


I am trying to understand the implicit function types from the link - http://www.scala-lang.org/blog/2016/12/07/implicit-function-types.html and below is a sample code as example. In the below code we first create a class Transaction.

class Transaction {
  private val log = scala.collection.mutable.ListBuffer.empty[String]
  def println(s: String): Unit = log += s

  private var aborted = false
  private var committed = false

  def abort(): Unit = { aborted = true }
  def isAborted = aborted

  def commit(): Unit =
  if (!aborted && !committed) {
     Console.println("******* log ********")
     log.foreach(Console.println)
     committed = true
  }
}

Next I define two methods f1 and f2 as shown below

def f1(x: Int)(implicit thisTransaction: Transaction): Int = {
  thisTransaction.println(s"first step: $x")
  f2(x + 1)
}
def f2(x: Int)(implicit thisTransaction: Transaction): Int = {
  thisTransaction.println(s"second step: $x")
  x
}

Then a method is defined to invoke the functions

def transaction[T](op: Transaction => T) = {
  val trans: Transaction = new Transaction
  op(trans)
  trans.commit()
}

Below lambda is used to invoke the code

transaction {
    implicit thisTransaction =>
      val res = f1(3)
      println(if (thisTransaction.isAborted) "aborted" else s"result: $res")
}  

My question is if I change the val trans: Transaction = new Transaction to implicit val thisTransaction = new Transaction and change op(trans) to op it is not working.

I am not able to understand why even if thisTransaction of type Transaction is present in the scope it is not being used?


Solution

  • This here compiles just fine with dotty 0.4.0-RC1:

    def transaction[T](op: implicit Transaction => T) = {
      implicit val trans: Transaction = new Transaction
      op
      trans.commit()
    }
    

    I think it should be clear from the introduction of the blog post that this is a new feature that Odersky invented to eliminate some boilerplate in the implementation of the Dotty compiler, quote:

    For instance in the dotty compiler, almost every function takes an implicit context parameter which defines all elements relating to the current state of the compilation.

    This seems to be currently not available in the mainstream versions of Scala.


    EDIT

    (answer to follow-up question in the comment)

    If I understood the blog post correctly, it is desugared into something like

    transaction(
      new ImplicitFunction1[Transaction, Unit] {
        def apply(implicit thisTransaction: Transaction): Unit = {
          val res = f1(args.length)(implicit thisTransaction:Transaction) 
          println(if (thisTransaction.isAborted) "aborted" else s"result: $res")
        }
      } 
    )
    

    the new ImplicitFunction1[...]{} is an anonymous local class. The base class ImplicitFunction1 is this new feature, which is similar to the Function for ordinary lambdas, but with implicit arguments.