2009-08-15 15 views
43

De día escribo C#. Todo lo que hago pasa por el análisis de código de Microsoft y las herramientas de análisis estático, por lo que mi C# tiene una estructura y un diseño muy regulares. Obviamente, escribo código con un cierto estilo. Es parcialmente, porque no tengo otra opción (no se compilará si pierdo un espacio antes de esa coma), pero también es bueno tener un código de búsqueda regular, saber dónde buscar cosas, etc.Elementos del estilo Scala?

At los fines de semana me estoy metiendo en Scala. Mirando la API de Scala y el origen del framework web Lift, obviamente no puedo ver ningún estilo estandarizado. Una cosa que salta a la vista, por ejemplo, es la falta de un archivo por clase por separado. La falta de consistencia con corchetes y llaves es otro ejemplo.

Entiendo que probablemente haya algunas razones para esto: en primer lugar, con el código de código abierto (o hobby) asegurarse de que un método obvio no esté completamente documentado es menos prioritario. En segundo lugar, cosas como las clases de casos reducen las declaraciones de clase de 20 líneas en una sola línea. En tercer lugar, C# es un lenguaje mucho más "plano": a menos que sea una declaración compleja LINQ, el número de paréntesis anidados, llaves y corchetes no es tan profundo. En Scala, las cosas tienden a quedar un poco anidadas.

¿Los usuarios habituales de Scala tienen un estilo específico al que se adhieren? ¿Estoy siendo estúpido al poner una clase de caso de una línea en su propio archivo por el bien de la convención [alien]? ¿Algun consejo?

+0

El Sr. Odersky dio una conferencia magistral en Scala Days 2013 en 'Scala with Style' - http://www.parleys.com/play/51c1994ae4b0d38b54f4621b/chapter0/about –

Respuesta

45

Tener múltiples clases y objetos dentro de un solo archivo se considera una buena forma en Scala, siempre que las clases estén estrechamente relacionadas.

Aunque no es necesario, se espera que el tipo devuelto por un método (una función nombrada declarada en un rasgo, clase u objeto) se declare para métodos no privados. Se esperan espacios después de :, pero no antes. Se espera que

// methods declared on a class, trait or object 
def length: Int = ... 
def multiply(other: Foo): Foo = ... 

def hypotenuse(a: Double, b: Double): Double = { 
    // function inside a method, so effectively private 
    def square(x: Double) = x * x 

    math.sqrt(square(a) + square(b)) 
} 

espacios entre las palabras clave y los paréntesis, pero no entre un nombre de método y el siguiente paréntesis, en la notación de punto. Para la notación del operador, no parece haber un estilo aceptado con respecto a los paréntesis, o cuándo usar esa notación, para el caso, pero se esperan espacios alrededor de métodos no alfanuméricos en dicha notación.

// keywords 
if (foo) ... 

// dot notation 
foo.doSomething(bar) 

// operator notation 
foo doSomething bar 
foo + bar 

Excepcionalmente, cuando la concatenación de cadenas con +, el estilo es recomendable no utilizar espacios a su alrededor. Por ejemplo:

// concatenate strings 
println("Name: "+person.name+"\tAge: "+person.age) 

Declaraciones que pueden ser de una sola línea se espera que sean de una sola línea, a menos que la anidación no es obvia.

// one-liners 
lazy val foo = calculateFoo 
def square(x: Int) = x * x 

métodos que no esperaría parámetros, y no tienen efectos secundarios, se supone que deben ser utilizados sin paréntesis, a excepción de los métodos de Java, que se espera para ser utilizado con paréntesis. Se supone que los métodos sin parámetros con efectos secundarios se usan con paréntesis. Se espera que

// without side-effects 
val x = foo.length 
val y = bar.coefficient 

// with side-effects 
foo.reverse() 

Las declaraciones que contiene una sola expresión no debe ser encerrado dentro de llaves a menos que otras consideraciones sintácticas hacen eso imposible. Se acepta el encerrar una expresión entre paréntesis para habilitar expresiones de varias líneas, pero he visto poco uso de eso.

// single-line expression 
def sum(list: List[Int]): Int = if (!list.isEmpty) list reduceLeft (_ + _) else 0 

// multi-line expression 
val sum = (
    getItems 
    reduceLeft (_ + _) 
) 

En-comprensiones para mantener, generadores y condiciones verticalmente alineados parece ser un estilo aceptado. En cuanto a yield, lo he visto alineado con for y sangrado.

// for-comprehensions 
val squares = 
    for (x <- numbers) 
    yield x * x 

// Curly brackets-style identation 
val cells = for { 
    x <- columns 
    y <- rows 
    if x != y 
} yield Cell(x, y) 

// Parameter-style identation 
val cells = for (x <- columns; 
       y <- rows; 
       if x != y) 
      yield Cell(x, y) 

También se acepta el estilo para alinear verticalmente los parámetros de una declaración de clase.

Hablando de sangría, dos espacios es la convención aceptada.

Se espera que las llaves se inicien en la misma línea de la declaración y terminen alineadas verticalmente con esa línea por sí mismo.

// another example 
def factorial(n: Int): Int = { 
    def fact(n: Int, acc: Int): Int = n match { 
    case 0 => acc 
    case x => fact(x - 1, x * acc) 
    } 

    fact(n, 1) 
} 

Para los procedimientos de - funciones cuyo tipo de retorno es Unit -, el estilo esperado se suponía que había que dejar de lado el tipo del método y el signo igual:

// procedures 
def complain { 
    println("Oh, no!") 
} 

Algunas personas piensan Sin embargo, este estilo es propenso a errores, ya que un signo igual perdido no cambiará una función que devuelve algo que no sea Unit en un procedimiento.

Los identificadores están escritos en camel case (p. Ej .: identifiersHaveHumps), como en Java. Para nombres de campos, parámetros de métodos, variables locales y funciones, comience con una letra minúscula. Para clases, rasgos y tipos, comience con una letra mayúscula.

Saliendo de la convención de Java son nombres constantes. En Scala, la práctica es usar una caja de camello estándar que comience con una letra mayúscula. Por ejemplo, Pi y no PI, XOffset y no X_OFFSET. Esta regla generalmente es seguida por cualquier singleton. Tener unas constantes y únicos pueden representar de esta manera tiene una consecuencia práctica, para el caso de los partidos:

import scala.Math.Pi 

val pi = Pi // this identifier will be shadowed by the identifier in the function below 

def isPi(n: Double): Boolean = n match { 
    case Pi => println("I got a true Pi."); true 
    case pi => println("I got "+pi+" and bounded it to an identifier named pi."); false 
} 

Los nombres de paquetes se escriben comenzando con una letra minúscula. Esto es particularmente útil cuando se distingue en un extracto de importación qué es un paquete y qué no. En el ejemplo anterior, Math no es un paquete (es un singleton), ya que comienza con una letra mayúscula.

No se recomienda el uso del carácter de subrayado - _ -, ya que ese carácter tiene muchos significados especiales en Scala. Estas reglas para identificadores se pueden encontrar en las páginas 141 y 142 de Programación en Scala, por Odersky, Spoon & Venners.

En este momento, no puedo recordar otras situaciones, pero no dude en pedir aclaraciones sobre puntos específicos. Algunas de estas reglas fueron explícitamente establecidas, otras son un consenso de la comunidad. Traté de dejar de lado mis propias preferencias, pero puede haber fallado.

Más importante aún, quizás no exista realmente una gran convención unificada. Una de las razones puede ser que Scala atraiga a personas de diferentes orígenes, como expertos en lenguaje funcional, programadores de Java y entusiastas de la web 2.0.

+3

En realidad, creo que el estilo de" dejar de lado los iguales " es * menos * propenso a errores ya que es efectivamente una abreviatura para anotar explícitamente la Unidad. La intención es muy clara, y es mucho más conciso que una anotación explícita. Sin embargo, vale la pena señalar que los métodos * all * efectivos deben declararse entre paréntesis, especialmente los de arity-1. Los métodos sin paréntesis son * solo * para funciones puras y accesadores de propiedad lógica. –

+0

Ok, corregiré la respuesta. De hecho, lo haré comunidad. Sin embargo, hubo algunas discusiones sobre cómo hacer que los métodos y funciones sin parámetros * no * usen paréntesis en todos los casos. –

+4

De hecho, el libro Programming in Scala recomienda que se puede omitir el paréntesis de un método si el método no tiene argumentos * y * no tiene efectos secundarios (por ejemplo, al acceder al estado de un objeto). Por lo tanto, una llamada como 'println()' aún debería tener el paréntesis, pero 'queue.size' o' array.length' no (consulte el principio Uniform access - http://en.wikipedia.org/wiki/ Uniform_access_principle). El último ejemplo sería 'queue.size()' y 'array.length' en java, que no respeta este principio. –

2

Esta es una pregunta muy importante. En general, el estilo de Scala parece recogido simplemente pasando el rato con otros miembros de la comunidad de Scala, leyendo el código fuente de Scala, etc.Eso no es muy útil para los recién llegados al idioma, pero sí indica que existe algún tipo de estándar de facto (según lo elegido por la sabiduría de las masas). Actualmente estoy trabajando en una guía de estilo totalmente realizada para Scala, que documenta las convenciones y mejores prácticas elegidas por la comunidad. Sin embargo, a) aún no ha terminado, yb) aún no estoy seguro de poder publicarlo (lo estoy escribiendo para el trabajo).

Para responder a su segunda pregunta (tipo de): en general, cada clase/rasgo/objeto debe tener su propio archivo nombrado de acuerdo con las convenciones de nomenclatura de Java. Sin embargo, en situaciones en las que tiene muchas clases que comparten un único concepto común, a veces es más fácil (tanto a corto como a largo plazo) colocarlas todas en el mismo archivo. Cuando lo haga, el nombre del archivo debería comenzar con una letra minúscula (aún camelCase) y ser descriptivo de ese concepto compartido.

+0

Haz, por favor, indica cualquier error en mi respuesta. No quiero propagar información errónea sobre el estilo: ya estamos suficientemente dispersos. –

2

Realmente no me importa el estilo típico que usan muchos de los codificadores Scala, así que solo aplico el mismo estándar que usaría en C#, Java o especialmente JavaScript.

Scala puede ser muy expresivo, pero usar un formato desconocido aumentará su barrera de entrada. Especialmente considerando interna DSLs, que posiblemente no podría tener un "Estándar".

Por lo tanto, le pido que haga lo que sea más legible para usted y su equipo.

12

Ahora hay una guía completa de estilo Scala que ha sido proposed to the community. Ni siquiera es ni remotamente oficial, pero es la única (que yo sepa) codificación de las convenciones aceptadas por la comunidad.

+4

La última versión está aquí: http://davetron5000.github.com/scala-style/ – robinst

+0

Realmente debería haber más fundamento detrás de las decisiones en esta guía de estilo. – sdkfasldf

+0

Creo que [los nuevos artículos de Li Haoyi] (http://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLeastPower.html) merecen un reconocimiento. – metasim

Cuestiones relacionadas