17

I think Scala goes too far from simplicity, like its syntax. For example Martin Odersky wrote the method in his book :

def calculate(s: String): Int =
  if (cache.contains(s))
    cache(s)
  else {
    val acc = new ChecksumAccumulator
    for (c <- s)
      acc.add(c.toByte)
    val cs = acc.checksum()
    cache += (s -> cs)
    cs
  }

If the methods grows, it becomes very painful to read the code, I can't match curly braces, can't fold the method in IDE. Is there any Scala coding conventions out there? I feel it's too flexible to express a simple method:

def add(b: Byte): Unit = {
  sum += b
}

def add(b: Byte): Unit = sum += b

def add(b: Byte) { sum += b }
Sawyer
  • 15,581
  • 27
  • 88
  • 124

7 Answers7

35

"If the method grows it becomes very painful to read the code". I think part of the answer is that methods should not grow. The functional programing style is to have many small methods.The calculate method is already on the large side.

To answer the more general questions about style guides for Scala programing: Here's a representative example.

Martin Odersky
  • 20,470
  • 9
  • 51
  • 49
  • This is especially easy because you can define methods within methods! A great way to break your code up. And much nicer than private companion methods for recursive functions as in Java. – schmmd Oct 09 '11 at 18:44
27

Here is a link to the Scala Style Guide.


The Curly Braces section says:

Curly-Braces:

Curly-braces should be omitted in cases where the control structure represents a pure- functional operation and all branches of the control structure (relevant to if/else) are single-line expressions. Remember the following guidelines:

  • if - Omit braces if you have an else clause. Otherwise, surround the contents with curly braces even if the contents are only a single line.

  • while - Never omit braces (while cannot be used in a pure-functional manner).

  • for - Omit braces if you have a yield clause. Otherwise, surround the contents with curly-braces, even if the contents are only a single line.

  • case - Omit braces if the case expression ts on a single line. Otherwise, use curly braces for clarity (even though they are not required by the parser).

    val news = if (foo)
      goodNews()
    else
      badNews()
    
    if (foo) {
      println("foo was true")
    }
    
    news match {
      case "good" => println("Good news!")
      case "bad" => println("Bad news!")
    }
    

I wish people followed this style guide :(


Please note that I don't agree with "Omit braces if if has an else clause" part. I'd much prefer to see the code like this:

def calculate(s: String): Int = {
  if (cache.contains(s)) {
    cache(s)
  } else {
    val acc = new ChecksumAccumulator
    for (c <- s) {
      acc.add(c.toByte)
    }
    val cs = acc.checksum()
    cache += (s -> cs)
    cs
  }
}
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 1
    brackets helps to format the codes, I also like your styles, maybe I just come from Java. – Sawyer Sep 15 '10 at 15:02
  • I don't know why a convention would ever demand superfluous punctuation. – Randall Schulz Sep 15 '10 at 17:05
  • @Randall: In Java world it does, and for a good reason: it makes code easier to refactor, and read (in my opinion). – missingfaktor Sep 15 '10 at 17:17
  • 3
    @Mising Faktor: To me, use of punctuation not demanded by the syntax is usually just unwanted noise. I do not see how extra braces, e.g., help refactoring. – Randall Schulz Sep 15 '10 at 20:21
  • 1
    Braces help to indicate scoping. If you never have a small area for code in your IDE or terminal, that's probably not important to you, however I frequently have this situation. – Radtoo Sep 16 '10 at 12:37
  • @RandallSchulz Haven't we learnt enough from bugs that came from missing surrounding brackets? All this flexibility to save a character or two is stupid, imho there should be one way and one way only to do things. All these alternatives and different ways to define functions and stuff just make code unmaintainable and difficult for new programmers to learn. – jbx May 14 '14 at 00:32
  • @jbx: I completely disagree. Punctuation is code noise and should be minimized. Given the fact that Scala's if / else is an *expresion*, not a statement and what I consider to be a completely reasonable expectation of attention to detail among *professional* programmers, minimizing punctuation is the proper preference. As for "one right way," I also disagree completely. That's the desire of a hobbyist programmer. – Randall Schulz May 14 '14 at 15:15
  • 1
    @RandallSchulz I guess its opinion. Having clear scope boundaries is making things unambiguous not noise. Its the same like the obsession of Scala functions to have arguments named meaningless names like `z`, `s`, `p` rather than a proper name, as if an extra couple of characters cost something. If the "one right way" is not good, why do all professional companies adopt coding conventions, rather than allowing the flexibility offered by the language? Making the code consistent between programmers makes it more readable and more maintainable, especially in larger code bases. – jbx May 14 '14 at 17:36
  • @jbx: I'd like to see some data to support your assertions. The fact that something is a common practice in no way constitutes evidence of its effectiveness. – Randall Schulz May 14 '14 at 19:20
7

The official guide is at https://docs.scala-lang.org/style/ (adopted from now removed http://davetron5000.github.io/scala-style/index.html, see Web Archive).

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
4

The particular example that you quote may look complex because it is using a cache to memoize the results. If you remove the memoization, the method reduce to:

def calculate(s: String): Int = {
    val acc = new ChecksumAccumulator
    for (c <- s)
        acc.add(c.toByte)
    acc.checksum()
}

which I think, is not complex at all.

Abhinav Sarkar
  • 23,534
  • 11
  • 81
  • 97
2

Here is the word by the man who wrote scala, himself, and still the main implementor of this language.

Source: Functional Programming Principles in Scala by Martin Odersky

you are also invited to take his course now or the next offering link[1]

===========================

Scala Style Guide Help

On this page you can find a list of common issues that we detected while looking at some submissions.

Some of the style issues can be detected by the automated style checker that we also use for the grading process. The style checker, which is based on Scalastyle, can be executed locally in sbt by running the styleCheck task.

Common Issues

1 Avoid Casts and Type Tests

Never use isInstanceOf or asInstanceOf - there’s always a better solution, both for the assignments, and also for any real-world Scala project. If you find yourself wanting to use casts, take a step back and think about what you’re trying to achieve. Re-read the assignment instructions and have another look at the corresponding lecture videos.

2 Indentation

Make sure your code is properly indented, it becomes a lot more readable.

This might seem trivial and not very relevant for our exercises, but imagine yourself in the future being part of a team, working on the same files with other coders: it is very important that everybody respects the style rules to keep the code healthy.

If your editor does not do indentation the way you would like it to have, you should find out how to change its settings. In Scala, the standard is to indent using 2 spaces (no tabs).

3 Line Length and Whitespace

Make sure the lines are not too long, otherwise your code is very hard to read. Instead of writing very long lines, introduce some local value bindings. Using whitespace uniformly makes your code more readable.

Example (long line, missing spaces):

if(p(this.head))this.tail.filter0(p, accu.incl(this.head))else this.tail.filter0(p, accu)

Better:

if (p(this.head))
  this.tail.filter0(p, accu.incl(this.head))
else
  this.tail.filter0(p, accu)

Even better (see #4 and #6 below):

val newAccu =

 if (p(this.head)) accu.incl(this.head)
  else accu
this.tail.filter0(p, newAccu)

4 Use local Values to simplify complex Expressions

When writing code in functional style, methods are often implemented as a combination of function calls. If such a combined expression grows too big, the code might become hard to understand.

In such cases it is better to store some arguments in a local value before passing them to the function (see #3 above). Make sure that the local value has a meaningful name (see #5 below)!

5 Choose meaningful Names for Methods and Values

The names of methods, fields and values should be carefully chosen so that the source code is easy to understand. A method name should make it clear what the method does. No, temp is never a good name :-)

A few improvable examples:

val temp = sortFuntion0(list.head, tweet)   // what does sortFunction0 do?
def temp(first: TweetSet, second : TweetSet): TweetSet = ...
def un(th: TweetSet,acc: TweetSet): TweetSet = ...
val c = if (p(elem)) accu.incl(elem) else accu
def loop(accu: Trending, current: TweetSet): Trending = ...
def help(ch: Char, L2: List[Char], compteur: Int): (Char, Int) = ...
def help2(L: List[(Char, Int)], L2: List[Char]): List[(Char, Int)] = ...

6 Common Subexpressions

You should avoid unnecessary invocations of computation-intensive methods. For example

this.remove(this.findMin).ascending(t + this.findMin)

invokes the this.findMin method twice. If each invocation is expensive (e.g. has to traverse an entire data structure) and does not have a side-effect, you can save one by introducing a local value binding:

val min = this.findMin
this.remove(min).ascending(t + min)

This becomes even more important if the function is invoked recursively: in this case the method is not only invoked multiple times, but an exponential number of times.

7 Don’t Copy-Paste Code!

Copy-pasting code is always a warning sign for bad style! There are many disadvantages:

The code is longer, it takes more time to understand it If the two parts are not identical, but very similar, it is very difficult to spot the differences (see example below) Maintaining two copies and making sure that they remain synchronized is very error-prone The amount of work required to make changes to the code is multiplied You should factor out common parts into separate methods instead of copying code around. Example (see also #3 above for another example):

val googleTweets: TweetSet = TweetReader.allTweets.filter(tweet =>
  google.exists(word => tweet.text.contains(word)))
val appleTweets: TweetSet = TweetReader.allTweets.filter(tweet =>
  apple.exists(word => tweet.text.contains(word)))
This code is better written as follows:

def tweetsMentioning(dictionary: List[String]): TweetSet =
  TweetReader.allTweets.filter(tweet =>
    dictionary.exists(word => tweet.text.contains(word)))

val googleTweets = tweetsMentioning(google)
val appleTweets  = tweetsMentioning(apple)

8 Scala doesn’t require Semicolons

Semicolons in Scala are only required when writing multiple statements on the same line. Writing unnecessary semicolons should be avoided, for example:

def filter(p: Tweet => Boolean): TweetSet = filter0(p, new Empty);

9 Don’t submit Code with “print” Statements

You should clean up your code and remove all print or println statements before submitting it. The same will apply once you work for a company and create code that is used in production: the final code should be free of debugging statements.

10 Avoid using Return

In Scala, you often don’t need to use explicit returns because control structures such as if are expressions. For example, in

def factorial(n: Int): Int = {
  if (n <= 0) return 1
  else return (n * factorial(n-1))
}

the return statements can simply be dropped.

11 Avoid mutable local Variables

Since this is a course on functional programming, we want you to get used to writing code in a purely functional style, without using side-effecting operations. You can often rewrite code that uses mutable local variables to code with helper functions that take accumulators. Instead of:

def fib(n: Int): Int = {
  var a = 0
  var b = 1
  var i = 0
  while (i < n) {
    val prev_a = a
    a = b
    b = prev_a + b
    i = i + 1
  }
  a
}

prefer:

def fib(n: Int): Int = {
  def fibIter(i: Int, a: Int, b: Int): Int =
    if (i == n) a else fibIter(i+1, b, a+b)
  fibIter(0, 0, 1)
}

12 Eliminate redundant “If” Expressions

Instead of

if (cond) true else false

you can simply write

cond

(Similarly for the negaitve case).

Other styling issues? Please post to the forum using the style or styleChecktags and we will augment this style guide with suggestions.

sivi
  • 10,654
  • 2
  • 52
  • 51
  • 2
    Point #5 is funny, given that the scaladoc is just full of argument names named `k`, `g`, `p`, and `z`. – jbx May 14 '14 at 00:21
  • I havn't noticed when I wrote that the maker of the language himself posted here. You might like to read his posts about Scala to vmore about the syntactical conventions. http://stackoverflow.com/a/3718851/1984636 – sivi Aug 12 '15 at 08:38
1

Yes, this is a bit late, but I wanted to post this as it reflects a real-world approach, one for "global readability and maintainability", and includes input from contributors to Apache Spark: Databricks Scala Style Guide

BgRva
  • 1,521
  • 12
  • 26
-1

There are a lot of conventions in this site http://docs.scala-lang.org/style/

Vu Anh
  • 955
  • 1
  • 18
  • 29