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.
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.
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 :-(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With