2012-07-15 10 views
5

Estoy tratando de hacer algo que no estoy seguro si el sistema de tipos de Scala me permitirá hacerlo.Crear cierre desde genérico en Scala

Básicamente quiero crear un cierre de una definición genérica y volver que el cierre, durante la ejecución de una función interna del mismo tipo.

Por ejemplo:

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 

ejemplo teórico de una definición:

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

Esto entonces me permite tener un sistema simple de crear devoluciones de llamada asincrónicas para SWT, manteniendo SWT de mi lógica de negocios.

+0

posible duplicado de [Scala genéricos: ¿por qué no puedo crear un objeto parametrizado dentro de la clase genérica?] (Http://stackoverflow.com/questions/5336648/scala-generics-why-i-cant-create-parametrised- object-inside-generic-class) –

+0

No creo que sea un 100% duplicado, porque incluso si creara una clase de tipo T con un método apply, igual tendría que tener en cuenta los parámetros proporcionados por T. Sin embargo, , el borrado de tipo puede hacer que este problema en particular sea imposible de resolver. – Hakkar

Respuesta

9

Usted puede hacer esto de manera más genérica con la biblioteca Shapeless definimos wrap de la siguiente manera:

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 

Y luego puede usarlo así:

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 

Esto funciona con una función de cualquier arity.

Por lo tanto, es posible en Scala, aunque hacer algo equivalente sin Shapeless sería casi seguro un enorme dolor de cabeza.

+0

Esto parece muy intrigante. Y no implica ninguna sobrecarga (significativa) del tiempo de ejecución, por ejemplo, al usar la reflexión? –

+0

No, no hay reflexión. Las clases tipo tendrán algunas en tiempo de ejecución, pero no debería ser excesivo. –

+0

Gracias. Eso es exactamente lo que quería. Esto podría convertirse en un patrón realmente útil para convertir clases anónimas a funciones anónimas. – Hakkar

2

¿Qué tal algo como lo siguiente:

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) 

Si la función que desee para envolver podría tener un número diferente de argumentos a continuación, usted, por desgracia, tiene que definir la función de su envoltura de una vez por cada aridad diferentes: - . (

+0

Gracias por la respuesta. La diferente cantidad de argumentos es desafortunada :(. Sin embargo, podría forzar al oyente a tomar una sola tupla y pasar el tipo de esa manera para evitar la limitación. Sería increíble poder reenviar el tipo genérico. Sin embargo, no estoy seguro si Scala tiene esa funcionalidad:/ – Hakkar

Cuestiones relacionadas