2009-07-16 16 views
5

Tengo un código de Scala que hace algo ingenioso con dos versiones diferentes de una función con parámetros de tipo. He simplificado mucho esto desde mi aplicación pero al final mi código está lleno de llamadas al formulario w(f[Int],f[Double]) donde w() es mi método mágico. Me encantaría tener un método más mágico como z(f) = w(f[Int],f[Double]) - pero no puedo obtener ninguna sintaxis como z(f[Z]:Z->Z) para que funcione como me parece (como a mí) como los argumentos de función no pueden tener sus propios parámetros de tipo. Aquí está el problema como un fragmento de código Scala.¿Puede Scala permitir parámetros de tipo libres en los argumentos (son Scala Type Parameters ciudadanos de primera clase?)?

¿Alguna idea? Una macro podría hacerlo, pero no creo que esos sean parte de Scala.

object TypeExample { 
    def main(args: Array[String]):Unit = { 
    def f[X](x:X):X = x    // parameterize fn 
    def v(f:Int=>Int):Unit = { }  // function that operates on an Int to Int function 
    v(f)        // applied, types correct 
    v(f[Int])      // appplied, types correct 
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions 
    w(f[Int],f[Double])    // works 
// want something like this: def z[Z](f[Z]:Z=>Z) = w(f[Int],f[Double]) 
// a type parameterized function that takes a single type-parameterized function as an 
// argument and then speicalizes the the argument-function to two different types, 
// i.e. a single-argument version of w() (or wrapper) 
    } 
} 
+0

¿Desea parametrizar el argumento de z? –

+0

Sí, me gustaría que el argumento z() sea una función parametrizada (como mi f) y luego tenga z() vincula el parámetro tipo a dos valores diferentes. – jmount

+0

Lo que sería totalmente rock sería un tipo de función que tomaría una función parametrizada f in y devolvería una clase con un miembro configurado en f [Int] y el otro en f [Doble]. – jmount

Respuesta

6

Puede hacerlo de esta manera:

trait Forall { 
    def f[Z] : Z=>Z 
} 

def z(u : Forall) = w(u.f[Int], u.f[Double]) 

o el uso de tipos estructurales:

def z(u : {def f[Z] : Z=>Z}) = w(u.f[Int], u.f[Double]) 

pero esto será más lenta que la primera versión, ya que utiliza la reflexión.

EDIT: Se trata de cómo se utiliza la segunda versión:

scala> object f1 {def f[Z] : Z=>Z = x => x} 
defined module f1 

scala> def z(u : {def f[Z] : Z=>Z}) = (u.f[Int](0), u.f[Double](0.0)) 
z: (AnyRef{def f[Z]: (Z) => Z})(Int, Double) 

scala> z(f1) 
res0: (Int, Double) = (0,0.0) 

Para la primera versión añadir f1 extends Forall o simplemente

scala> z(new Forall{def f[Z] : Z=>Z = x => x}) 
+0

Esto parece sensato (usando la representación de objetos de funciones), pero aún no sé lo suficiente sobre Scala para hacerlo funcionar. En cada caso, si escribe 'z (f)' obtengo los errores de compilación: falta de argumento para el método f; expresión polimórfica no se puede crear una instancia con el tipo esperado; – jmount

+0

He agregado un ejemplo de uso. –

+0

¡Eso es genial! Obviamente, lo que dijiste tenía sentido, simplemente no sabía lo suficiente como para aplicarlo. La línea que quería es def z (u: {def f [Z]: Z => Z}) = (u.f [Int], u.f [Doble]) que funciona perfectamente. La picazón filosófica que quería satisfacer era escribir "f" una sola vez (garantizar que ambas especializaciones tengan la misma función), agregar algunas declaraciones adicionales para lograr que no haya ningún problema en absoluto. Gracias. – jmount

2

No creo que lo que quieres hacer es posible.

Editar:

Mi versión anterior era defectuoso. Esto funciona:

scala> def z(f: Int => Int, g: Double => Double) = w(f, g) 
z: (f: (Int) => Int,g: (Double) => Double)Unit 

scala> z(f, f) 

Pero, por supuesto, es más o menos lo que tienes.

No creo que incluso sea posible que funcione, porque los parámetros de tipo solo existen en tiempo de compilación. En tiempo de ejecución no existe tal cosa. Por lo tanto, no tiene sentido para mí pasar una función parametrizada, en lugar de una función con los parámetros de tipo inferidos por Scala.

Y, no, Scala no tiene un sistema macro.

+0

Me gusta la parte donde Scala infiere los tipos (eso es bueno). Gracias por ayudarnos a explorar el espacio de la solución. – jmount

6

Si tienes curiosidad, lo que está hablando aquí se llama "polimorfismo de rango-k". See wikipedia. En su caso, k = 2. Algunos traducción:

Cuando se escribe

f[X](x : X) : X = ... 

entonces estás diciendo que f tiene tipo "forall XX -> X"

lo que quiere para z es tipo "(forall ZZ -> Z) -> Unidad". Ese par adicional de paréntesis es una gran diferencia. En términos del artículo de wikipedia, coloca el clasificador anterior antes de 2 flechas en lugar de solo 1. La variable de tipo no se puede crear una instancia solo una vez y se puede llevar a cabo, potencialmente tiene que crearse una instancia para muchos tipos diferentes. (Aquí "instanciación" no significa construcción de objeto, significa asignar un tipo a una variable de tipo para comprobación de tipo).

Como la respuesta de alexy_r muestra que esto es codificable en Scala utilizando objetos en lugar de tipos de funciones directas, esencialmente utilizando clases/rasgos como los parens. A pesar de que parece haber dejado colgando un poco en términos de conectarlo a su código original, por lo que aquí está:

// Este es el código

object TypeExample { 
    def main(args: Array[String]):Unit = { 
    def f[X](x:X):X = x    // parameterize fn 
    def v(f:Int=>Int):Unit = { }  // function that operates on an Int to Int function 
    v(f)        // applied, types correct 
    v(f[Int])      // appplied, types correct 
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions 
    w(f[Int],f[Double])    // works 

// Esto es nuevo código

trait ForAll { 
     def g[X](x : X) : X 
    } 

    def z(forall : ForAll) = w(forall.g[Int], forall.g[Double]) 
    z(new ForAll{def g[X](x : X) = f(x)}) 
    } 
} 
+0

Gracias, James lo investigaré. Definitivamente no tengo ninguna queja sobre el sistema de tipo Scala. Estoy dispuesto a trabajar con él para obtener lo que quiero. Y me encanta que el compilador me diga inmediatamente lo que no funcionará. Mi intención es la que dijiste: el problema original (no simplificado) es una función que se computa en diferentes campos algebraicos (algunos caros para trabajar, otros baratos para trabajar) y quería que Scala generara dos funciones especializadas de seguridad de tipo a partir de una plantilla. función inicial. Todas las otras partes móviles fueron fáciles de arreglar, excepto esta última transformación de tipo. – jmount

+0

'la sintaxis nueva de ForAll' es mejor que la que tenía. Olvidé que funciona. –

+0

Enlace de Wikipedia: http://en.wikipedia.org/wiki/Parametric_polymorphism#Higher-ranked_polymorphism –

Cuestiones relacionadas