2010-11-17 11 views
5

En C++ que puede hacer lo siguiente:Posible codificar tipos de devolución genéricos en Scala similares a las plantillas C++?

template<typename T, typename V> 
struct{ 
    void operator()(T _1, V _2){ 
     _2.foo(_1); 
    } 
}; 

que me permite arbitrariamente decido usar cualquier objeto que tiene un método llamado "foo" que tiene algún tipo de "T", sin especificar de antemano o bien el tipo de argumento de la función "foo" ni el tipo de retorno de dicha función.

Cuando miro Scala, ver rasgos como Function1, y estoy jugando con las definiciones de funciones como

def foo[T<:{def foo():Unit}](arg:T) = //something 
def bar(x:{def foo():Unit}) = //something 
def baz[T,V<:Function1[T,_]](x:T, y:V) = y(x) 

Me miro y pienso por qué no puedo hacer lo mismo? ¿Por qué "baz" devuelve un Any? ¿No puede deducir el tipo de devolución real en el momento de la compilación? ¿Por qué tengo que declarar el tipo de devolución de "foo" si ni siquiera puedo usarlo?

me gustaría ser capaz de hacer

def biff[T,V:{def foo(x:T):Unit}] = //something 

o

def boff[T<:{def foo(x:Double):_}](y:T) = y.foo _ 

Se puede hacer esto y solo estoy perdiendo algo? O si no, ¿por qué no?

+0

No tengo claro el ejemplo de 'biff'. ¿Hay un argumento perdido? –

+0

@Aaron Novstrup no falta ningún argumento. Me aparece un error cuando lo digo así. Dice que no puedo definir "V" de modo que dependa de "T". – wheaties

Respuesta

11

Actualización:

realidad, se puede hacer mucho mejor, y el tipo inferencer se ayuda que:

def boff[T,R](y: T)(implicit e: T <:< {def foo(x: Double): R}) = e(y).foo _ 

Para baz, la misma técnica mejorará la inferencia de tipos:

def baz[T,R,V](x:T, y:V)(implicit e: V <:< (T => R)) = e(y).apply(x) 

scala> baz(1, (i: Int) => i+1) 
res0: Int = 2 

Puede mejorar currying:

def baz[T,R](x: T)(f: T => R) = f(x) 

Primera solución:

El tipo inferencer no suministrará el tipo T para usted, pero usted puede hacer:

class Boff[T] { 
    def apply[R](y: T)(implicit e: T <:< {def foo(x: Double): R}) = e(y).foo _ 
} 
object boff { 
    def apply[T] = new Boff[T] 
} 
+0

+1 Eso es inteligente. –

2

Para foo:

def foo[R,T<:{def foo():R}](arg:T) = ... 

En el caso de baz, dices que V debe ser una función de T a algún tipo. Este tipo no puede aparecer en el tipo de resultado: ¿cómo podrías escribirlo? Entonces el compilador solo puede inferir que el tipo de resultado es Any. Si usted le da un nombre, sin embargo, se obtiene

scala> def baz[T,R,V<:Function1[T,R]](x:T, y:V) = y(x) 
baz: [T,R,V <: (T) => R](x: T,y: V)R 
6

La distinción fundamental entre la Scala y C++ es que cada clase en Scala se compila una vez, y luego se vuelve disponible como está para el uso con cualquier cosa que depende de él , mientras que una clase con plantilla en C++ debe compilarse para cada nueva dependencia.

Así que, de hecho, una plantilla de C++ genera N clases compiladas, mientras que Scala genera solo una.

¿No se puede deducir el tipo de devolución real en el momento de la compilación?

Porque debe decidirse en el momento en que se compila la clase, que puede ser diferente del momento en que se compila su uso.

+0

Gracias Daniel. Siempre ofreces un gran "por qué". – wheaties

Cuestiones relacionadas