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

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

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

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

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

Ответ 1

Наличие нескольких классов и объектов внутри одного файла считается хорошей формой в 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

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

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

//  with side-effects
foo.reverse()

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

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

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

В целях понимания, сохранение генераторов и условий, выровненных по вертикали, кажется приемлемым. Что касается yield, я видел, что он совмещен с for и отступом.

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

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

Говоря об отступе, два пространства являются принятым соглашением.

Ожидается, что фигурные скобки начинаются с одной и той же строки декларации и заканчиваются вертикально в соответствии с этой линией.

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

Для процедур - функций, тип возврата которых Unit -, ожидаемый стиль должен был исключать тип метода и знак равенства:

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

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

Идентификаторы записываются в виде верблюда (например: identifiersHaveHumps), как в Java. Для имен полей, параметров метода, локальных переменных и функций начинайте с строчной буквы. Для классов, признаков и типов начинайте с буквы верхнего регистра.

Отступ от соглашения Java - это постоянные имена. В Scala практика заключается в использовании стандартного верблюжьего корпуса, начинающегося с буквы верхнего регистра. Например Pi, а не Pi, XOffset и не X_OFFSET. За этим правилом обычно следует любой синглтон. Представление констант и синглтонов таким образом имеет практическое значение для совпадений:

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
}

Имена пакетов начинаются с буквы нижнего регистра. Это особенно полезно при различении в заявлении на импорт того, что такое пакет, а что нет. В предыдущем примере Math не является пакетом (это singleton), так как он начинается с буквы верхнего регистра.

Использование символа подчеркивания - _ - не рекомендуется, так как этот символ имеет много специальных значений в Scala. Эти правила для идентификаторов можно найти на страницах 141 и 142 программирования в Scala, Odersky, Spoon и Venners.

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

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

Ответ 4

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

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

Ответ 5

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

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

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