def data syntax scala

syntax - data - ¿Cuáles son las reglas precisas para cuando puede omitir paréntesis, puntos, llaves,=(funciones), etc.?



scala object (6)

Definiciones de clase:

val o var pueden omitirse de los parámetros de clase que harán que el parámetro sea privado.

Agregar var o val hará que sea público (es decir, se generan accesadores de métodos y mutadores).

{} se puede omitir si la clase no tiene cuerpo, es decir,

class EmptyClass

Instanciación de clase:

Los parámetros genéricos pueden omitirse si el compilador los puede inferir. Sin embargo, tenga en cuenta que, si sus tipos no coinciden, entonces el parámetro de tipo siempre se introduce para que coincida. Entonces, sin especificar el tipo, es posible que no obtenga lo que espera, es decir, dado

class D[T](val x:T, val y:T);

Esto le dará un error de tipo (Int found, expected String)

var zz = new D[String]("Hi1", 1) // type error

Mientras que esto funciona bien:

var z = new D("Hi1", 1) == D{def x: Any; def y: Any}

Porque el parámetro de tipo, T, se deduce como el supertipo menos común de los dos: Cualquiera.

Definiciones de funciones:

= puede soltarse si la función devuelve Unidad (nada).

{} para el cuerpo de la función se puede descartar si la función es una declaración única, pero solo si la declaración devuelve un valor (se necesita el signo = ), es decir,

def returnAString = "Hi!"

pero esto no funciona:

def returnAString "Hi!" // Compile error - ''='' expected but string literal found."

El tipo de devolución de la función se puede omitir si se puede inferir (un método recursivo debe tener su tipo de devolución especificado).

() se puede descartar si la función no toma ningún argumento, es decir,

def endOfString { return "myDog".substring(2,1) }

que por convención está reservado para métodos que no tienen efectos secundarios, más sobre eso más adelante.

() no se deja caer per se cuando se define un parámetro de pase por nombre , pero en realidad es una notación bastante semánticamente diferente, es decir,

def myOp(passByNameString: => String)

Dice que myOp toma un parámetro de paso por nombre, que da como resultado una Cadena (es decir, puede ser un bloque de código que devuelve una cadena) en oposición a los parámetros de la función,

def myOp(functionParam: () => String)

que dice que myOp toma una función que tiene cero parámetros y devuelve una cadena.

(Tenga en cuenta que los parámetros de paso por nombre se compilan en funciones, simplemente hace que la sintaxis sea más agradable).

() puede soltarse en la definición del parámetro de función si la función solo toma un argumento, por ejemplo:

def myOp2(passByNameString:(Int) => String) { .. } // - You can drop the () def myOp2(passByNameString:Int => String) { .. }

Pero si toma más de un argumento, debe incluir el ():

def myOp2(passByNameString:(Int, String) => String) { .. }

Declaraciones:

. puede soltarse para usar la notación del operador, que solo se puede usar para operadores de infijo (operadores de métodos que toman argumentos). Vea la respuesta de Daniel para más información.

  • . también se puede quitar para la cola de la lista de funciones de postfix

  • () puede soltarse para la lista de operadores de postfix.tail

  • () no se puede usar con métodos definidos como:

    def aMethod = "hi!" // Missing () on method definition aMethod // Works aMethod() // Compile error when calling method

Debido a que esta notación está reservada por convención para métodos que no tienen efectos secundarios, como List # tail (es decir, la invocación de una función sin efectos secundarios significa que la función no tiene ningún efecto observable, excepto por su valor de retorno).

  • () se puede descartar para notación de operador al pasar en un solo argumento

  • () puede ser necesario para usar operadores de postfix que no estén al final de una declaración

  • () puede ser necesario para designar declaraciones anidadas, fines de funciones anónimas o para operadores que toman más de un parámetro

Cuando se llama a una función que toma una función, no se puede omitir el () de la definición de la función interna, por ejemplo:

def myOp3(paramFunc0:() => String) { println(paramFunc0) } myOp3(() => "myop3") // Works myOp3(=> "myop3") // Doesn''t work

Cuando se llama a una función que toma un parámetro by-name, no se puede especificar el argumento como una función anónima sin parámetros. Por ejemplo, dado:

def myOp2(passByNameString:Int => String) { println(passByNameString) }

Debe llamarlo así:

myOp("myop3")

o

myOp({ val source = sourceProvider.source val p = myObject.findNameFromSource(source) p })

pero no:

myOp(() => "myop3") // Doesn''t work

OMI, el uso excesivo de tipos de devolución puede ser dañino para que el código se reutilice. Basta con mirar las especificaciones para obtener un buen ejemplo de legibilidad reducida debido a la falta de información explícita en el código. La cantidad de niveles de direccionamiento indirecto para descubrir realmente cuál es el tipo de una variable puede ser una locura. Es de esperar que mejores herramientas puedan evitar este problema y mantener nuestro código conciso.

(OK, en la búsqueda para compilar una respuesta más completa y concisa (si me he perdido algo, u obtuve algo incorrecto / inexacto, por favor comento), he agregado el principio de la respuesta. Tenga en cuenta que este no es un idioma especificación, así que no estoy tratando de hacerlo exactamente correcto académicamente, simplemente más como una tarjeta de referencia).

¿Cuáles son las reglas precisas para cuando puede omitir (omitir) paréntesis, puntos, llaves, = (funciones), etc.?

Por ejemplo,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).

  • service es mi objeto
  • def findAllPresentations: Option[List[Presentation]]
  • votes devuelve List[Vote]
  • must y be son ambas funciones de las especificaciones

¿Por qué no puedo ir?

(service findAllPresentations get first votes size) must be equalTo(2)

?

El error del compilador es:

"RestServicesSpecTest.this.service.findAllPresentations of type Option [List [com.sharca.Presentation]] no toma parámetros"

¿Por qué cree que estoy tratando de pasar un parámetro? ¿Por qué debo usar puntos para cada llamada de método?

Por qué debe (service.findAllPresentations get first votes size) ser igualTo (2) dar como resultado:

"no encontrado: valor primero"

Sin embargo, el "debe ser igual a 2" de (service.findAllPresentations.get.first.votes.size) debe ser igual a 2, es decir, ¿el método de encadenamiento funciona bien? - cadena de cadena de objeto param.

Revisé el libro y el sitio web de Scala y no puedo encontrar una explicación completa.

De hecho, como explica Rob H en Stack Overflow, ¿qué personajes puedo omitir en Scala? , que es el único caso de uso válido para omitir el ''.'' es para las operaciones de estilo de "operando de operador operando", y no para el método de encadenamiento?


En realidad, en segunda lectura, tal vez esta es la clave:

Con métodos que solo tienen un único parámetro, Scala permite al desarrollador reemplazar el. con un espacio y omita los paréntesis

Como se menciona en la publicación del blog: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 .

Entonces, tal vez esto sea en realidad un "azúcar de sintaxis" muy estricto que solo funciona donde se llama efectivamente un método, en un objeto, que toma un parámetro . p.ej

1 + 2 1.+(2)

Y nada más.

Esto explicaría mis ejemplos en la pregunta.

Pero como dije, si alguien pudiera señalar exactamente dónde se especifica en la especificación del idioma, sería muy apreciado.

De acuerdo, un buen tipo (paulp_ de #scala) ha señalado en qué especificación del lenguaje está esta información:

6.12.3: La precedencia y la asociatividad de los operadores determinan la agrupación de partes de una expresión de la siguiente manera.

  • Si hay varias operaciones infija en una expresión, los operadores con mayor precedencia se unirán más estrechamente que los operadores con menor precedencia.
  • Si hay operaciones infija consecutivas e0 op1 e1 op2. . .opn en con los operadores op1,. . . , con la misma prioridad, todos estos operadores deben tener la misma asociatividad. Si todos los operadores son asociativos de izquierda, la secuencia se interpreta como (... (e0 op1 e1) op2 ...) opn en. De lo contrario, si todos los operadores son correctos asociativos, la secuencia se interpreta como e0 op1 (e1 op2 (.opop en) ...).
  • Los operadores de Postfix siempre tienen menor prioridad que los operadores de infijo. Por ejemplo, e1 op1 e2 op2 siempre es equivalente a (e1 op1 e2) op2.

El operando de la derecha de un operador asociado a la izquierda puede consistir en varios argumentos encerrados entre paréntesis, por ej. E op (e1,.., En). Esta expresión se interpreta como e.op (e1,.., En).

Una operación binaria asociativa izquierda e1 op e2 se interpreta como e1.op (e2). Si op es correcto, asociativo, la misma operación se interpreta como {val x = e1; e2.op (x)}, donde x es un nombre nuevo.

Hmm - para mí no encaja con lo que estoy viendo o simplemente no lo entiendo;)


Me resulta más fácil seguir esta regla de oro: en las expresiones los espacios se alternan entre métodos y parámetros. En su ejemplo, (service.findAllPresentations.get.first.votes.size) must be equalTo(2) analiza como (service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2)) . Tenga en cuenta que los paréntesis alrededor de los 2 tienen una asociatividad más alta que los espacios. Los puntos también tienen una mayor asociatividad, por lo que (service.findAllPresentations.get.first.votes.size) must be.equalTo(2) analizará como (service.findAllPresentations.get.first.votes.size).must(be.equalTo(2)) .

service findAllPresentations get first votes size must be equalTo 2 como service.findAllPresentations(get).first(votes).size(must).be(equalTo).2 .


No hay ninguno. Es probable que reciba consejos sobre si la función tiene o no efectos secundarios. Esto es falso. La corrección es no usar efectos secundarios en la medida razonable permitida por Scala. En la medida en que no puede, todas las apuestas están apagadas. Todas las apuestas El uso de paréntesis es un elemento del conjunto "todo" y es superfluo. No proporciona ningún valor una vez que todas las apuestas están desactivadas.

Este consejo es esencialmente un intento de un sistema de efectos que falla (no debe confundirse con: es menos útil que otros sistemas de efectos).

Intenta no hacer efecto secundario. Después de eso, acepte que todas las apuestas están apagadas. Ocultarse detrás de una notación sintáctica de facto para un sistema de efectos puede y solo causa daño.


Parece que has encontrado la respuesta. De todos modos, intentaré dejarlo en claro.

Puede omitir punto al usar las notaciones de prefijo, infijo y postfijo, la llamada notación de operador . Mientras usa la notación del operador, y solo entonces, puede omitir el paréntesis si hay menos de dos parámetros pasados ​​al método.

Ahora, la notación de operador es una notación para method-call , lo que significa que no se puede usar en ausencia del objeto al que se llama.

Detallaré brevemente las anotaciones.

Prefijo:

Solo ~ ! , + y - se pueden usar en notación de prefijo. Esta es la notación que está usando cuando escribe !flag o val liability = -debt .

Infijo:

Esa es la notación donde aparece el método entre un objeto y sus parámetros. Todos los operadores aritméticos encajan aquí.

Postfix (también sufijo):

Esa notación se usa cuando el método sigue a un objeto y no recibe parámetros . Por ejemplo, puede escribir list tail , y esa es la notación postfija.

Puede encadenar llamadas de notación infija sin problema, siempre que no se curry ningún método. Por ejemplo, me gusta usar el siguiente estilo:

(list filter (...) map (...) mkString ", " )

Eso es lo mismo que:

list filter (...) map (...) mkString ", "

Ahora, ¿por qué estoy usando paréntesis aquí, si el filtro y el mapa toman un solo parámetro? Es porque les paso funciones anónimas. No puedo mezclar definiciones de funciones anónimas con estilo infijo porque necesito un límite para el final de mi función anónima. Además, la definición del parámetro de la función anónima puede interpretarse como el último parámetro del método infijo.

Puedes usar infix con múltiples parámetros:

string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")

Las funciones al curry son difíciles de usar con notación infija. Las funciones de plegado son un claro ejemplo de eso:

(0 /: list) ((cnt, string) => cnt + string.size) (list foldLeft 0) ((cnt, string) => cnt + string.size)

Necesita usar paréntesis fuera de la llamada infija. No estoy seguro de las reglas exactas en juego aquí.

Ahora, hablemos de postfix. Postfix puede ser difícil de usar, ya que nunca se puede usar en ninguna parte excepto al final de una expresión . Por ejemplo, no puedes hacer lo siguiente:

list tail map (...)

Porque cola no aparece al final de la expresión. Usted no puede hacer esto tampoco:

list tail length

Puede usar la notación infija usando paréntesis para marcar el final de las expresiones:

(list tail) map (...) (list tail) length

Tenga en cuenta que se desaconseja la notación postfix porque puede no ser segura .

Espero que esto haya aclarado todas las dudas. Si no, solo deje caer un comentario y veré qué puedo hacer para mejorarlo.


Una colección de citas que dan una idea de las diversas condiciones ...

Personalmente, pensé que habría más en la especificación. Estoy seguro de que debe haber, solo que no estoy buscando las palabras correctas ...

Sin embargo, hay un par de fuentes y las he recopilado juntas, pero nada realmente completo / comprensible / comprensible / eso explica los problemas anteriores para mí ...

"Si el cuerpo de un método tiene más de una expresión, debe rodearlo con llaves {...}. Puede omitir las llaves si el cuerpo del método tiene solo una expresión".

Desde el capítulo 2, "Escribir menos, hacer más", de la programación de Scala :

"El cuerpo del método superior viene después del signo igual ''=''. ¿Por qué un signo igual? ¿Por qué no simplemente llaves {...}, como en Java? Porque punto y coma, tipos de retorno de función, listas de argumentos de método e incluso las llaves a veces se omite, usar un signo igual previene varias posibles ambigüedades de análisis. Usar un signo igual también nos recuerda que las funciones pares son valores en Scala, lo cual es consistente con el soporte de Scala de la programación funcional, descrito en más detalle en el Capítulo 8, Programación Funcional en Scala ".

Del capítulo 1, "Zero to Sixty: Introducing Scala", de Programming Scala :

"Una función sin parámetros se puede declarar sin paréntesis, en cuyo caso se debe invocar sin paréntesis. Esto brinda soporte para el Principio de acceso uniforme, de modo que la persona que llama no sepa si el símbolo es una variable o una función sin parámetros.

El cuerpo de la función está precedido por "=" si devuelve un valor (es decir, el tipo de retorno es distinto de la Unidad), pero el tipo de retorno y el "=" pueden omitirse cuando el tipo es Unidad (es decir, se ve como un procedimiento a diferencia de una función).

No se requieren frenos alrededor del cuerpo (si el cuerpo es una sola expresión); más precisamente, el cuerpo de una función es solo una expresión, y cualquier expresión con múltiples partes debe estar entre llaves (una expresión con una parte puede incluirse entre corchetes).

"Las funciones con cero o un argumento pueden invocarse sin el punto y los paréntesis. Pero cualquier expresión puede tener paréntesis a su alrededor, por lo que puede omitir el punto y aún así usar paréntesis.

Y como puede utilizar llaves en cualquier lugar, puede usar paréntesis, puede omitir el punto y poner llaves, que pueden contener varias declaraciones.

Las funciones sin argumentos se pueden llamar sin los paréntesis. Por ejemplo, la función length () en String se puede invocar como "abc" .length en lugar de "abc" .length (). Si la función es una función de Scala definida sin paréntesis, entonces la función debe invocarse sin paréntesis.

Por convención, las funciones sin argumentos que tienen efectos secundarios, como println, se llaman con paréntesis; aquellos sin efectos secundarios son llamados sin paréntesis ".

De la publicación de blog Scala Syntax Primer :

"Una definición de procedimiento es una definición de función donde el tipo de resultado y el signo igual se omiten, su expresión de definición debe ser un bloque. Por ejemplo, def f (ps) {stats} es equivalente a def f (ps): Unit = {stats }.

Ejemplo 4.6.3 Aquí hay una declaración y una definición de un procedimiento llamado write:

trait Writer { def write(str: String) } object Terminal extends Writer { def write(str: String) { System.out.println(str) } }

El código anterior se completa implícitamente con el siguiente código:

trait Writer { def write(str: String): Unit } object Terminal extends Writer { def write(str: String): Unit = { System.out.println(str) } }"

De la especificación del lenguaje:

"Con los métodos que solo tienen un único parámetro, Scala permite al desarrollador reemplazar el .con un espacio y omitir los paréntesis, lo que permite la sintaxis del operador que se muestra en nuestro ejemplo de operador de inserción. Esta sintaxis se usa en otros lugares en la API de Scala, como la construcción de instancias de rango:

val firstTen:Range = 0 to 9

Aquí nuevamente, a (Int) es un método vainilla declarado dentro de una clase (en realidad hay algunas conversiones de tipo más implícitas, pero obtienes la deriva) ".

De Scala for Java Refugees Part 6: Getting Over Java :

"Ahora, cuando intentas" m 0 ", Scala descarta que sea un operador unario, sobre la base de que no es válido (~,!, - y +). Encuentra que" m "es un objeto válido - es una función, no un método, y todas las funciones son objetos.

Como "0" no es un identificador de Scala válido, no puede ser un infijo ni un operador de postfijo. Por lo tanto, Scala se queja de que esperaba ";" - que separaría dos expresiones (casi) válidas: "m" y "0". Si lo inserta, entonces se quejaría de que m requiere un argumento o, en su defecto, un "_" para convertirlo en una función parcialmente aplicada ".

"Creo que el estilo de sintaxis del operador solo funciona cuando tienes un objeto explícito en el lado izquierdo. La sintaxis está pensada para que puedas expresar las operaciones de estilo del operando del operador del operando de forma natural".

¿Qué personajes puedo omitir en Scala?

Pero lo que también me confunde es esta cita:

"Tiene que haber un objeto para recibir una llamada a un método. Por ejemplo, no se puede hacer" println "¡Hola mundo!" "Ya que la lista de impresión necesita un destinatario de objeto. Puede hacer "Println Console" Hello World! "" Que satisface la necesidad ".

Porque, por lo que puedo ver, hay un objeto para recibir la llamada ...