斯卡拉风格的元素?

时间:2009-08-15 14:07:44

标签: scala

白天我写C#。我所做的一切都通过Microsoft代码分析和静态分析工具进行,因此我的C#具有非常规则的结构和布局。显然,我用一定的风格编写代码。部分原因是因为我没有选择(如果我在逗号之前错过了一个空格就不会编译),但是定期看代码,知道在哪里寻找东西等等也很好。

周末我要进入斯卡拉。查看Scala API和Lift Web框架源代码,我无法看到任何标准化样式。例如,跳出来的一件事是每个类缺少一个单独的文件。与括号和括号缺乏一致性是另一个例子。

我理解推动这个问题可能有几个原因:首先,使用开源(或爱好)代码确保明确的方法没有完全记录,不是优先考虑的事情。其次,像case类这样的东西会将20行的类声明减少为一行。第三,C#是一种非常“平坦”的语言:除非它是一个复杂的LINQ语句,嵌套的parens,括号和括号的数量并不那么深。在Scala中,事情往往会有点嵌套。

常规Scala用户是否具有他们坚持的特定风格?为了[异化]惯例,我只是把一个单行的案例类放在自己的文件中愚蠢吗?有什么提示吗?

5 个答案:

答案 0 :(得分:45)

在单个文件中包含多个类和对象在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返回到过程中。

标识符是用camel case编写的(例如: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
}

包名称以小写字母开头。当在import语句中区分什么是包而什么不包时,这尤其有用。在前面的示例中,Math不是包(它是单例),因为它以大写字母开头。

不建议使用下划线字符 - _ - 因为该字符在Scala中有许多特殊含义。这些标识符规则可以在Scala编程的第141和142页找到,由Odersky,Spoon&amp;凡纳斯。

目前,我不记得其他情况,但可以随时要求澄清具体问题。其中一些规则已明确说明,其他则更多是社区共识。我试图忽略自己的偏好,但我可能失败了。

更重要的是,或许,实际上并没有太多统一的惯例。其中一个原因可能是Scala吸引了许多不同背景的人,例如功能语言专家,Java程序员和Web 2.0爱好者。

答案 1 :(得分:12)

现在有一个完整的Scala风格指南,proposed to the community。它甚至还没有远程官方,但它是社区接受的公约的唯一(据我所知)编纂。

答案 2 :(得分:7)

现在 Scala Style Guide 可用。它不是100%官方(位于“Community-driven documentation for Scala”网站上),但似乎是Scala最标准的风格指南。

答案 3 :(得分:2)

这是一个非常重要的问题。一般来说,Scala样式似乎只是通过与Scala社区的其他成员闲逛,阅读Scala源代码等来获取。这对该语言的新手来说并不是很有帮助,但它确实表明某种事实上的标准< em>确实存在(由群众的智慧选择)。我目前正在为Scala制作一个完全实现的样式指南,该指南记录了社区选择的约定和最佳实践。然而,a)它尚未完成,b)我不确定我是否会被允许发布它(我正在为工作而写它)。

回答第二个问题(某种程度上):通常,每个类/特征/对象都应该根据Java命名约定获得自己的文件。但是,在您有许多共享一个共同概念的类的情况下,有时将它们全部放入同一文件中是最容易的(在短期和长期)。当你这样做时,文件的名称应该以小写字母(仍然是camelCase)开头,并描述该共享概念。

答案 4 :(得分:2)

我并不关心许多Scala程序员使用的典型样式,因此我只使用与C#,Java或特别是JavaScript相同的标准。

Scala 可以非常富有表现力,但使用不熟悉的格式会增加您的入门门槛。特别是考虑内部DSLs,它不可能有“标准”。

所以,我说要做任何事情,让你和你的团队更容易理解你的代码。