SAM conversion with receiver support

I want to do some thing like the Kotlin’s [Function literals with receiver] i(https://kotlinlang.org/docs/lambdas.html#function-literals-with-receiver) in Scala 2

Eg:

public object EmitterTest {
    public abstract class EmitterOps<I, O> {
        public fun emit(out: O): Unit = TODO()
        public abstract fun apply(i: I): Unit
    }

    public inline fun <I, O> transform(crossinline emitterOps: EmitterOps<I, O>.(value: I) -> Unit): Unit = TODO()

    public fun test() {
        transform<Int, String> { value ->
            //here the emit method is called on the receiver EmitterOps
            emit("")
        }
    }
}

But I can’t do it in Scala 2

object Test {
  def transform[I, O](emitterOps: EmitterOps[I, O]) = ???

  def test(): Unit = {
    // OK
    val emitterOps: EmitterOps[Int, String] = new EmitterOps[Int, String] {
      override def apply(in: Int): Unit = {
        emit("hello")
      }
    }

    // ----------
    // ERROR
    val emitterOps2: EmitterOps[Int, String] = (in: Int) => {
      // ERROR here
      emit("hello")
    }
    transform[Int, String](emitterOps)

    // -------
    // With context
    val emitterWithContext: EmitterOps[Int, String] => EmitterOps[Int, String] = receiver => {
      value =>
        {
          println(value)
          receiver.emit(value.toString)
        }
    }
    // bind to emitterOps
    transform(emitterWithContext(emitterOps))
  }

  abstract class EmitterOpsWithContext[I, O] extends EmitterOps[I, O] {}

  abstract class EmitterOps[I, O] {
    def emit(out: O): Unit = {
      ???
    }

    def apply(in: I): Unit
  }
}

Below is the best I can get.

object Main extends App {
  trait FlowCollector[T] {
    def emit(value: T): Unit
  }

  object FlowCollector {
    implicit class FlowCollectorOps[T](collector: FlowCollector[T]) {
      def emit(value: T): Unit = {
        collector.emit(value)
      }
    }
  }

  case class Flow[T](data: Seq[T]) {
    def collect(f: FlowCollector[T] => Unit): Unit = {
      val collector = new FlowCollector[T] {
        override def emit(value: T): Unit = {
          println(value)
        }
      }
      f(collector)
    }
  }

  val flow = Flow(Seq(1, 2, 3, 4, 5))

  flow.collect { implicit collector =>
    collector.emit(1)
    collector.emit(2)
  }

}

Is there anyway in Scala I can achieve this, in the lambda block, I can refer to the other methods defined in that type.

This can’t be done in Java too, just tried, and below is the real code:


    unsafeTransformUnordered[T](parallelism) { implicit emitter => out =>
      import EmitterOps._
      import EmitterUnsafeOps._
      val future = f(out)
      future.value match {
        case Some(elem) => handleNow(elem)
        case None       => future.onComplete(handle(_))(pekko.dispatch.ExecutionContexts.parasitic)
      }
    }

refs: Simpler take on builder pattern and context functions

That is indeed not possible. The only things that bring things into scope, besides explicit definitions, are class/object/trait/new through inheritance, and imports.

1 Like

Seems like Kotlin support this with high order function.
T.() -> Unit is actually something like T => () => (),

Kotlin is a different language, with different rules.

If you’re trying to create a lightweight DSL, you may want to look at Scala’s context functions,
https://docs.scala-lang.org/scala3/reference/contextual/context-functions.html

Thanks, I think I can’t achive the same in Scala 3 with context function, because I have to import the Context with given too.