Элементы стиля Scala?

Днем я пишу C#. Все, что я делаю, проходит через инструменты анализа кода и статического анализа Microsoft, поэтому мой C# имеет очень регулярную структуру и структуру. Очевидно, я пишу код с определенным стилем. Это частично, потому что у меня нет выбора (он не скомпилируется, если я пропущу пробел перед этой запятой), но также неплохо иметь регулярно выглядящий код, зная, где искать вещи и т. Д.

По выходным я вхожу в Скалу. Глядя на исходный код веб-фреймворка Scala API и Lift, я не вижу ни одного стандартизированного стиля. Например, одна вещь, которая бросается в глаза, это отсутствие отдельного файла для каждого класса. Отсутствие согласованности со скобками и фигурными скобками является еще одним примером.

Я понимаю, что, вероятно, этому есть несколько причин: во-первых, с открытым исходным кодом (или хобби), чтобы убедиться, что очевидный метод не полностью документирован, является меньшим приоритетом. Во-вторых, такие вещи, как case-классы, сокращают 20-строчные объявления классов в одну строку. В-третьих, C# является гораздо более "плоским" языком: если это не сложный оператор LINQ, число вложенных скобок, скобок и скобок не так уж глубоко. В Scala вещи, как правило, немного вложены.

У обычных пользователей Scala есть определенный стиль, который они придерживаются? Разве я просто глуп, помещая однострочный case-класс в свой собственный файл ради [чужой] конвенции? Какие-нибудь советы?

5 ответов

Решение

Наличие нескольких классов и объектов в одном файле считается хорошей формой в Scala, если классы тесно связаны между собой.

Хотя это и необязательно, тип, возвращаемый методом - именованной функцией, объявленной в признаке, классе или объекте, - как ожидается, будет объявлен для не закрытых методов. Пробелы ожидаются после :, но не до этого.

//  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))
}

Пробелы ожидаются между ключевыми словами и круглыми скобками, но не между именем метода и следующими круглыми скобками в точечной записи. Для нотации оператора, кажется, нет принятого стиля в отношении круглых скобок - или когда использовать эту нотацию, если на то пошло, но в таких нотациях ожидаются пробелы вокруг не алфавитно-цифровых методов.

//  keywords
if (foo) ...

//  dot notation
foo.doSomething(bar)

//  operator notation
foo doSomething bar
foo + bar

Исключительно, при объединении строк с +Рекомендуемый стиль - не использовать пробелы вокруг него. Например:

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

Ожидается, что объявления, которые могут быть однострочными, будут однострочными, если вложение не является очевидным.

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

Methods that do not expect parameters, and do not have side effects, are supposed to be used without parenthesis, except for Java methods, which are expected to be used with parenthesis. Parameter-less methods with side effects are supposed to be used with parenthesis.

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

//  with side-effects
foo.reverse()

Declarations which contains a single expression are expected not to be enclosed inside curly braces unless other syntactic considerations make that impossible. Enclosing an expression within parenthesis to enable multi-line expressions is accepted, but I have seen little use of that.

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

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

In for-comprehensions, keeping generators and conditions vertically aligned seems to be an accepted style. Что касается yield, I have seen it both aligned with for and indented.

//  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)

It's also accepted style to vertically align parameters of a class declaration.

Speaking of indentation, two-spaces is the accepted convention.

Curly braces are expected to start on the same line of the declaration, and end vertically aligned with that line by itself.

//  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)
}

For procedures -- functions whose return type is Unit --, the expected style was supposed to be to leave out the type of the method and the equal sign:

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

Some people think this style is error prone, however, as a missed equal sign will change a function returning something other than Unit into a procedure.

Identifiers are written in camel case (eg: identifiersHaveHumps), like in Java. For names of fields, method parameters, local variables and functions, start with a lower case letter. For classes,traits and types, start with an upper case letter.

Departing from Java convention are constant names. In Scala, the practice is to use standard camel case starting with an upper case letter. Например Pi и не PI, XOffset and not X_OFFSET, This rule is usually followed by any singleton. Having a constants and singletons be represented that way has a practical consequence, for case matches:

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
}

Package names are written beginning with a lower case letter. This is particularly helpful when distinguishing in an import statement what is a package and what is not. В предыдущем примере Math is not a package (it's a singleton), as it begins with an upper case letter.

Using the underline character -- _ -- is not recommended, as that character has many special meanings in Scala. These rules for identifiers can be found on pages 141 and 142 of Programming in Scala, by Odersky, Spoon & Venners.

Прямо сейчас я не могу вспомнить другие ситуации, но не стесняйтесь просить разъяснения по конкретным вопросам. Некоторые из этих правил были сформулированы в явном виде, другие - скорее консенсус сообщества. Я пытался оставить свои предпочтения, но, возможно, потерпел неудачу.

Что еще более важно, возможно, просто нет единой конвенции. Одной из причин этого может быть то, что Scala привлекает людей из самых разных слоев общества, таких как специалисты по функциональному языку, программисты на Java и энтузиасты веб 2.0.

В настоящее время существует полное руководство по стилю Scala, которое было предложено сообществу. Это даже не официально официально, но это единственная (насколько мне известно) кодификация принятых сообществом соглашений.

Теперь доступно руководство по стилю Scala. Он не является на 100% официальным (находится на сайте " Документация для Scala ", разработанным сообществом), но кажется самым стандартным руководством по стилю для Scala.

Это очень важный вопрос. В общем, стиль Scala, кажется, подхватывается просто общением с другими членами сообщества Scala, чтением исходного кода Scala и т. Д. Это не очень полезно для новичков в языке, но это указывает на то, что некоторый стандарт де-факто делает существуют (по выбору мудрости масс). В настоящее время я работаю над полностью реализованным руководством по стилю для Scala, которое документирует выбранные сообществом соглашения и лучшие практики. Однако, а) он еще не закончен, и б) я еще не уверен, что мне будет разрешено опубликовать его (я пишу это для работы).

Чтобы ответить на ваш второй вопрос (своего рода): в общем, каждый класс / признак / объект должен получить свой собственный файл, названный в соответствии с соглашениями об именах Java. Однако в ситуациях, когда у вас много классов, которые разделяют одну общую концепцию, иногда проще (как в краткосрочной, так и в долгосрочной перспективе) поместить их все в один файл. Когда вы это сделаете, имя файла должно начинаться со строчной буквы (все еще camelCase) и описывать эту общую концепцию.

Меня не волнует типичный стиль, который используют многие кодеры Scala, поэтому я просто применяю тот же стандарт, который использовал бы в C#, Java или особенно JavaScript.

Scala может быть очень выразительным, но использование незнакомого формата увеличит ваш барьер для входа. Особенно учитывая внутренние DSL, которые не могли иметь "Стандарт".

Итак, я говорю делать все, чтобы ваш код был более читабельным для вас и вашей команды.

Другие вопросы по тегам