2010-05-17 7 views
7

Esta es una violación problemática de la seguridad de tipo en mi proyecto, por lo que estoy buscando una forma de desactivarla. Parece que si una función toma un AnyRef (o un java.lang.Object), puede llamar a la función con cualquier combinación de parámetros, y Scala fusionará los parámetros en un objeto Tuple e invocará la función.Scala fusiona los parámetros de llamadas a múltiples funciones en un Tuple. ¿Se puede deshabilitar?

En mi caso, la función no espera un Tuple y falla en tiempo de ejecución. Esperaría que esta situación fuera atrapada en tiempo de compilación.

object WhyTuple { 
def main(args: Array[String]): Unit = { 
    fooIt("foo", "bar") 
} 
def fooIt(o: AnyRef) { 
    println(o.toString) 
} 
} 

de salida:

(foo,bar) 
+4

Bueno, si una función toma un 'AnyRef' como argumento, espera que el argumento sea cualquier cosa, ¿no? Quiero decir, incluso si scala no empaqueta automáticamente los argumentos en una tupla, aún sería capaz de pasar una tupla explícitamente, lo cual es perfectamente seguro para los tipos, porque su función toma cualquier cosa y las tuplas son cualquier cosa. Si su función solo puede manejar ciertos tipos de argumentos, debe declararse que solo tome estos tipos de argumentos o verificar dinámicamente el tipo de argumento. – sepp2k

+0

Entonces, ¿qué espera? Es posible que deba dividirlo en varios especialistas. Por supuesto, si estás esperando algo más que Tuple, eso no sería de ayuda. – sblundy

+1

Bastante justo ... pero generalmente el contrato (en otros lenguajes de programación) es si una función espera 1 parámetro y se llama con 2, entonces la compilación fallará. –

Respuesta

1

La compilación es capaz de interpretar métodos sin paréntesis. Por lo tanto, toma los corchetes de la ronda en el sentido de Tuple. Su llamada es lo mismo que:

fooIt(("foo","bar")) 

Dicho esto, se puede hacer que el método para excluir la llamada, y recuperar el valor si se utiliza algún envoltorio al igual que algunos (AnyRef) o TUPLE1 (AnyRef).

+3

A menos que me falta algo, esta no es una respuesta sino un resumen de la premisa de la pregunta. – sepp2k

0

Creo que la definición de (x, y) en Predef es responsable. El indicador del compilador "-Y-no-predefs" puede ser de alguna utilidad, suponiendo que estés dispuesto a hacer el trabajo de importar manualmente cualquier implicito que necesites. Con eso quiero decir que tendrás que agregar scala.Predef._ de importación por todo el lugar.

+1

-1 Las teorías son geniales, pero ¿por qué no probarlas primero? – retronym

4

Editar: De acuerdo con las personas mejor informadas que yo, la siguiente respuesta es realmente incorrecta: ver this answer. Gracias Aaron Novstrup por señalar esto.

Esto es en realidad un capricho del analizador , no del sistema de tipo o el compilador. Scala permite que se invoquen funciones de cero o de una arg sin paréntesis, pero no funciones con más de un argumento. Entonces, como Fred Haslam says, lo que ha escrito no es una invocación con dos argumentos, es una invocación con un argumento de tupla-valor. Sin embargo, si el método hizo toma dos argumentos, la invocación sería sería una invocación de dos arg. Parece que el significado del código afecta la forma en que analiza (que es un poco suckful).

En cuanto a lo que realmente puede hacer al respecto, es complicado. Si el método realmente requería dos argumentos, este problema desaparecería (es decir, si alguien intentara llamarlo por error con un argumento o con tres, obtendría un error de compilación como esperaba). ¿No crees que hay algún parámetro adicional que has estado posponiendo para agregar a ese método? :)

+2

FYI, si agrega más parámetros a la * llamada *, acaba con tuplas de nivel superior: fooIt ("foo", "bar", "jam") produce Tuple3. {comentando la llamada, no la firma del método} –

+0

Buen punto, voy a editar para aclarar eso. –

+1

Desafortunadamente, esta respuesta terminó con la mayoría de los votos (en el momento de esta publicación). En realidad es incorrecto - vea [esto] (http://stackoverflow.com/questions/2850902/scala-coalesces-multiple-function-call-parameters-into-a-tuple-can-this-be-dis/2852147# 2852147) y [este] (http://stackoverflow.com/questions/5997553/why-and-how-is-scala-treating-a-tuple-specially-when-calling-a-one-arg-function/5998559 # 5998559) –

4

¿Qué pasa algo como esto:

object Qx2 { 
    @deprecated def callingWithATupleProducesAWarning(a: Product) = 2 
    def callingWithATupleProducesAWarning(a: Any) = 3 
} 

tuplas tienen el rasgo de producto, por lo que cualquier llamada a callingWithATupleProducesAWarning que pasa una tupla producirá un aviso de desaprobación.

+1

Lindo, pero legítimamente puede querer pasar un valor de tipo 'Producto' (piense en cualquier clase de caso) al método. – retronym

+0

Es cierto: estoy siendo flojo. Creo que deberá agregar n métodos individuales para todos los tipos de TupleN a los que desea proteger si necesita pasar un Producto en uso normal. –

7

No implicits o Predef en juego aquí en absoluto - solo buena magia de compilador pasada de moda. Puede encontrarlo en el type checker. No puedo ubicarlo en la especificación en este momento.

Si está lo suficientemente motivado, podría agregar una opción -X al compilador para evitar esto.

Como alternativa, puede evitar escribir los métodos arity-1 que aceptan un supertipo de TupleN.

+0

http://lampsvn.epfl.ch/trac/scala/ticket/3583 – retronym

0

¿Podría también agregar una anulación de dos param, que evitaría que el compilador aplicara el azúcar sintáctico? Al hacer que los tipos sean adecuadamente oscuros, es poco probable que obtenga falsos positivos. E.g:

object WhyTuple { 

    ... 

    class DummyType 

    def fooIt(a: DummyType, b: DummyType) { 
    throw new UnsupportedOperationException("Dummy function - should not be called") 
    } 
} 
+0

Por supuesto, esto no es muy escalable si tiene muchos de esos métodos, o si necesita hacerlo por más de 2 parámetros. – pdbartlett

Cuestiones relacionadas