2012-07-15 7 views
5

Ich versuche, etwas zu tun, dass ich nicht sicher bin, ob Scala Typ-System mir gestatten zu tun.Erstellen Schließung von Generic in Scala

Ich möchte im Grunde eine Schließung von einer generischen Definition erstellen und diese Schließung zurückgeben, während eine Funktion innerhalb des gleichen Typs ausführen.

Zum Beispiel:

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 

Theoretische Beispiel für eine 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) 
       } 
      } 
      }) 
     } 
    } 

Dies würde dann erlauben Sie mir zu schaffen asynchrone Rückrufe für SWT ein einfaches System zu haben, während SWT halten aus meiner Geschäftslogik.

+0

möglich Duplikat [Scala Generika - warum ich nicht parametrisierte Objekt innerhalb generische Klasse erstellen] (http://stackoverflow.com/questions/5336648/scala-generics-why-i-cant-create-parametrised- Objekt-inside-generic-Klasse) –

+0

ich glaube nicht, es ist ein 100% doppelte, denn selbst wenn ich eine Klasse des Typs T mit einer Anwendung Methode erstellen, würde ich immer noch in den richtigen Parametern durch T. Anspruch nehmen wollen, müssen jedoch , Typ Löschung kann dieses spezielle Problem unmöglich zu lösen machen. – Hakkar

Antwort

9

Sie können dies tun allgemeiner mit der Shapeless Bibliothek Wir definieren wrap wie folgt:

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 

Und dann kann es wie folgt verwendet werden:

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 

Dies funktioniert mit einer Funktion jeder Arity.

So ist es möglich, in Scala, obwohl etwas zu tun, ohne Äquivalent Shapeless würde mit ziemlicher Sicherheit eine enorme Kopfschmerzen sein.

+0

Diese sehr faszinierend aussieht und es zieht keinen (signifikanten) Laufzeit-Overhead, zB durch Reflexion mit –

+0

Nö, keine Reflexion Die Typklassen einige haben?. Laufzeit-Overhead, sollte aber nicht übertrieben sein –

+0

Danke, genau das, was ich wollte, könnte ein sehr nützliches Muster für die Umwandlung anonymer Klassen in anonyme Funktionen werden. – Hakkar

2

Wie wäre es etwas in dieser Richtung:

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) 

Wenn die Funktion, die Sie wickeln wollen eine unterschiedliche Anzahl von Argumenten haben könnte, dann werden Sie leider müssen Ihre Wrap-Funktion einmal für jedes unterschiedliche arity definieren: - . (

+0

Danke für die Antwort. Die unterschiedliche Anzahl von Argumenten Teil ist bedauerlich :(. Obwohl Sie den Hörer zwingen könnte ein einzelnes Tupel zu nehmen und in der Art, dass die Art und Weise um die Begrenzung zu bekommen passieren. Es wäre genial der Lage sein, den generischen Typ zu übermitteln. I obwohl ist nicht sicher, ob Scala diese Funktionalität hat:.!/ – Hakkar