Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Closure from Generic in Scala

Tags:

scala

swt

I'm trying to do something that I'm not sure if Scala's type system will allow me to do.

I basically want to create a closure from a generic definition and return that closure, while executing a function internally of the same type.

For example:

val f = async[(str:String, i:Int, b:BigInt) => Unit]({ (String, Int, BigInt) =>
  // Code here...
})

// 'f' would have a type of (String, Int, BigInt) => Unit and would wrap the passed anonymous function

Theoretical example of a definition:

  def async[T](
    shell: Shell,
    success: T,
    failure: (Throwable) => Unit): T = {
        new T {
          val display = shell.getDisplay()
          display.asyncExec(new Runnable() {
            def run(): Unit = {
              try {
                success(_)
              } catch {
                case e:Throwable =>
                  failure(e)
              }
            }
          })
        }
  }

This would then allow me to have a simple system of creating asynchronous callbacks for SWT, while keeping SWT out of my business logic.

like image 796
Hakkar Avatar asked Jul 15 '12 22:07

Hakkar


2 Answers

You can do this more generically with the Shapeless library. We define wrap as follows:

import shapeless._, Functions._

def wrap[F, A <: HList, R](f: F)(implicit
  h: FnHListerAux[F, A => R],
  u: FnUnHListerAux[A => R, F]
): F = { (args: A) => 
  println("Before f")
  val result = f.hlisted(args)
  println("After f")
  result
}.unhlisted

And then can use it like this:

scala> val sum: (Int, Int) => Int = _ + _
sum: (Int, Int) => Int = <function2>

scala> val wrappedSum = wrap(sum)
wrappedSum: (Int, Int) => Int = <function2>

scala> wrappedSum(100, 1)
Before f
After f
res0: Int = 101

This works with a function of any arity.

So it is possible in Scala, although doing something equivalent without Shapeless would almost certainly be an enormous headache.

like image 89
Travis Brown Avatar answered Oct 24 '22 04:10

Travis Brown


How about something along these lines:

scala> def wrap[T1, T2, T3, R](f: (T1, T2, T3) => R) = {
 |   (v1: T1, v2: T2, v3: T3) =>
 |     println("Before f")
 |     val r = f(v1, v2, v3)
 |     println("After f")
 |     r
 | }
wrap: [T1, T2, T3, R](f: (T1, T2, T3) => R)(T1, T2, T3) => R

scala> def foo(x: String, y: Int, z: BigInt) = (x, y, z)
foo: (x: String, y: Int, z: BigInt)(String, Int, BigInt)

scala> val wrapped = wrap(foo _)
wrapped: (String, Int, BigInt) => (String, Int, BigInt) = <function3>

scala> wrapped("foo", 42, 12345)
Before f
After f
res0: (String, Int, BigInt) = (foo,42,12345)

If the function you want to wrap could have different numbers of arguments then you will, unfortunately, have to define your wrap function once for each different arity :-(

like image 33
Paul Butcher Avatar answered Oct 24 '22 05:10

Paul Butcher