2012-07-15 92 views
5

我正在嘗試做一些我不確定Scala的類型系統是否允許我這樣做的事情。在Scala中創建從泛型閉包

我基本上想創建一個通用定義的閉包,並返回該閉包,同時執行一個內部的相同類型的函數。

例如:

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 

的定義理論例如:

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) 
       } 
      } 
      }) 
     } 
    } 

這則讓我有創造SWT異步回調的一個簡單的系統,同時保持SWT出來的我商業邏輯。

+0

可能的重複[斯卡拉泛型 - 爲什麼我不能在泛型類中創建參數化對象?](http://stackoverflow.com/questions/5336648/scala-generics-why-i-cant-create-parametrised- object-inside-generic-class) – 2012-07-15 23:49:14

+0

我不認爲它是100%重複的,因爲即使我用apply方法創建了一個類型爲T的類,我仍然需要接受由T提供的適當參數。 ,類型擦除可能會使這個特定的問題無法解決。 – Hakkar 2012-07-16 02:24:16

回答

9

您可以用Shapeless庫做到這一點更一般我們定義wrap如下:

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 

然後可以像這樣使用它:

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 

這適用於任何arity的函數。

所以在Scala中是可能的,儘管做一些沒有無形體的東西幾乎肯定會讓人頭疼。

+0

這看起來非常有趣!它不會導致任何(顯着的)運行時開銷,例如,通過使用反射? – 2012-07-16 06:54:43

+0

不,沒有反射。運行時間開銷,但不應該過多 – 2012-07-16 07:00:12

+0

謝謝,這正是我想要的,這可能會成爲將匿名類轉換爲匿名函數的非常有用的模式。 – Hakkar 2012-07-16 21:15:37

2

如何沿着這些路線的東西:

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) 

如果你想換行可以有爭論,那麼你將很不幸,有一次定義您的包裹功能爲每個不同的元數不同數量的功能: - (

+0

感謝您的回答。不同數量的參數部分是不幸的:(儘管如此,你可以強制這個監聽器採用一個單獨的元組,並通過這種類型的方式來繞過這個限制,能夠轉發泛型類型是非常棒的。我不確定Scala是否有這樣的功能:/。 – Hakkar 2012-07-16 02:19:30