Skip to content

Commit 6388c68

Browse files
author
Alexandru Nedelcu
committed
1 parent 530b381 commit 6388c68

File tree

3 files changed

+111
-43
lines changed

3 files changed

+111
-43
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ contribute.
3030
- [2.7. MUST NOT throw exceptions for validations of user input or flow control](sections/2-language-rules.md#27-must-not-throw-exceptions-for-validations-of-user-input-or-flow-control)
3131
- [2.8. MUST NOT catch Throwable](sections/2-language-rules.md#28-must-not-catch-throwable-when-catching-exceptions)
3232
- [2.9. MUST NOT use "null"](sections/2-language-rules.md#29-must-not-use-null)
33-
- [2.10. MUST NOT use Java's Date or Calendar, instead use Joda-Time or JSR-310](sections/2-language-rules.md#210-must-not-use-javas-date-or-calendar-instead-use-joda-time-or-jsr-310)
34-
- [2.11. SHOULD NOT use Any or AnyRef or isInstanceOf / asInstanceOf](sections/2-language-rules.md#211-should-not-use-any-or-anyref-or-isinstanceof--asinstanceof)
35-
- [2.12. MUST serialize dates as either Unix Timestamp or ISO 8601](sections/2-language-rules.md#212-must-serialize-dates-as-either-unix-timestamp-or-as-iso-8601)
36-
- [2.13. MUST NOT use magic values](sections/2-language-rules.md#213-must-not-use-magic-values)
37-
- [2.14. SHOULD NOT use "var" as shared state](sections/2-language-rules.md#214-should-not-use-var-as-shared-state)
33+
- [2.10. MUST NOT use "Option.get"](sections/2-language-rules.md#210-must-not-use-option-get)
34+
- [2.11. MUST NOT use Java's Date or Calendar, instead use Joda-Time or JSR-310](sections/2-language-rules.md#211-must-not-use-javas-date-or-calendar-instead-use-joda-time-or-jsr-310)
35+
- [2.12. SHOULD NOT use Any or AnyRef or isInstanceOf / asInstanceOf](sections/2-language-rules.md#212-should-not-use-any-or-anyref-or-isinstanceof--asinstanceof)
36+
- [2.13. MUST serialize dates as either Unix Timestamp or ISO 8601](sections/2-language-rules.md#213-must-serialize-dates-as-either-unix-timestamp-or-as-iso-8601)
37+
- [2.14. MUST NOT use magic values](sections/2-language-rules.md#214-must-not-use-magic-values)
38+
- [2.15. SHOULD NOT use "var" as shared state](sections/2-language-rules.md#215-should-not-use-var-as-shared-state)
39+
- [2.16. Public functions SHOULD have an explicit return type](sections/2-language-rules.md#216-public-functions-should-have-an-explicit-return-type)
3840

3941
- [3. Application Architecture](sections/3-architecture.md)
4042
- [3.1. SHOULD NOT use the Cake pattern](sections/3-architecture.md#31-should-not-use-the-cake-pattern)

sections/2-language-rules.md

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -417,10 +417,54 @@ list.flatMap(x => Some(x).filter(_ % 2 == 0))
417417
// => 2,4,6
418418
```
419419

420-
Also, never, ever use `option.get`. That's just sloppy engineering and
421-
defeats the purpose of using Option.
420+
### 2.10. MUST NOT use `Option.get`
422421

423-
### 2.11. SHOULD NOT use Any or AnyRef or isInstanceOf / asInstanceOf
422+
You might be tempted to do this:
423+
424+
```scala
425+
val someValue: Option[Double] = ???
426+
427+
// ....
428+
val result = someValue.get + 1
429+
```
430+
431+
Don't ever do that, since your trading a `NullPointerException` for a
432+
`NoSuchElementException` and that defeats the purpose of using
433+
`Option` in the first place.
434+
435+
Alternatives:
436+
437+
1. using `Option.getOrElse`
438+
2. using `Option.fold`
439+
3. using pattern matching and dealing with the `None` branch explicitly
440+
4. not taking the value out of its optional context
441+
442+
As an example for (4), not taking the value out of its context means
443+
this:
444+
445+
```scala
446+
val result = someValue.map(_ + 1)
447+
```
448+
449+
### 2.11. MUST NOT use Java's Date or Calendar, instead use Joda-Time or JSR-310
450+
451+
Java's Date and Calendar classes from the standard library are awful
452+
because:
453+
454+
1. resulting objects are mutable, which doesn't make sense for
455+
expressing a date, which should be a value (how would you feel if
456+
you had to work with StringBuffer everywhere you have Strings?)
457+
2. months numbering is zero based
458+
3. Date in particular does not keep timezone info, so Date values are completely useless
459+
4. it doesn't make a difference between GMT and UTC
460+
5. years are expressed as 2 digits instead of 4
461+
462+
Always use [Joda-Time](http://www.joda.org/joda-time/) - of if you can
463+
afford to switch to Java 8, there's a shinny new
464+
[JSR-310](http://www.threeten.org/) that's based on Joda-Time and that
465+
will be the new standard once people adopt Java 8.
466+
467+
### 2.12. SHOULD NOT use Any or AnyRef or isInstanceOf / asInstanceOf
424468

425469
Avoid using Any or AnyRef or explicit casting, unless you've got a
426470
really good reason for it. Scala is a language that derives value from
@@ -467,25 +511,7 @@ json match {
467511
}
468512
```
469513

470-
### 2.10. MUST NOT use Java's Date or Calendar, instead use Joda-Time or JSR-310
471-
472-
Java's Date and Calendar classes from the standard library are awful
473-
because:
474-
475-
1. resulting objects are mutable, which doesn't make sense for
476-
expressing a date, which should be a value (how would you feel if
477-
you had to work with StringBuffer everywhere you have Strings?)
478-
2. months numbering is zero based
479-
3. Date in particular does not keep timezone info, so Date values are completely useless
480-
4. it doesn't make a difference between GMT and UTC
481-
5. years are expressed as 2 digits instead of 4
482-
483-
Always use [Joda-Time](http://www.joda.org/joda-time/) - of if you can
484-
afford to switch to Java 8, there's a shinny new
485-
[JSR-310](http://www.threeten.org/) that's based on Joda-Time and that
486-
will be the new standard once people adopt Java 8.
487-
488-
### 2.12. MUST serialize dates as either Unix timestamp, or as ISO 8601
514+
### 2.13. MUST serialize dates as either Unix timestamp, or as ISO 8601
489515

490516
Unix timestamps, provided that we are talking about the number of
491517
seconds or milliseconds since 1970-01-01 00:00:00 UTC (with emphasis
@@ -496,7 +522,7 @@ is a decent serialization format supported by most libraries.
496522
Avoid anything else and also when storing dates without a timezone
497523
attached (like in MySQL), always express that info in UTC.
498524

499-
### 2.13. MUST NOT use magic values
525+
### 2.14. MUST NOT use magic values
500526

501527
Although not uncommon in other languages to use "magic" (special)
502528
values like `-1` to signal particular outcomes, in Scala there are a
@@ -511,7 +537,7 @@ Don't do this:
511537
val index = list.find(someTest).getOrElse(-1)
512538
```
513539

514-
### 2.14. SHOULD NOT use "var" as shared state
540+
### 2.15. SHOULD NOT use "var" as shared state
515541

516542
Avoid using "var" at least when speaking about shared mutable
517543
state. Because if you do have shared state expressed as vars, you'd
@@ -544,3 +570,45 @@ in the case of an atomic reference means spin loops. But it will save
544570
you from lots and lots of headaches later. And it's best to avoid
545571
mutation entirely.
546572

573+
### 2.16. Public functions SHOULD have an explicit return type
574+
575+
Prefer this:
576+
577+
```scala
578+
def someFunction(param1: T1, param2: T2): Result = {
579+
???
580+
}
581+
```
582+
583+
To this:
584+
585+
```scala
586+
def someFunction(param1: T1, param2: T2) = {
587+
???
588+
}
589+
```
590+
591+
Yeah, type inference on the result of a function is great and all, but
592+
for public methods:
593+
594+
1. it's not OK to rely on an IDE or to inspect the implementation in
595+
order to see the returned type
596+
2. Scala currently infers the most specialized type possible, because
597+
in Scala the return type on functions is covariant, so you might
598+
actually get a really ugly type back
599+
600+
For example, what is the returned type of this function:
601+
602+
```scala
603+
def sayHelloRunnable(name: String) = new Runnable {
604+
def sayIt() = println(s"Hello, $name")
605+
def run() = sayIt()
606+
}
607+
```
608+
609+
Do you think it's `Runnable`?
610+
Wrong, it's `Runnable{def sayIt(): Unit}`.
611+
612+
As a side-effect, this also decreases compilation times, as whenever
613+
`sayHelloRunnable` changes implementation, it also changes the
614+
signature so everything that depends on it must be recompiled.

sections/5-actors.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,9 @@ class MyActor extends Actor {
4747
}
4848
}
4949

50-
object MyActor {
51-
case class Add(key: String)
52-
case class Contains(key: String)
53-
}
50+
// Messages
51+
case class Add(key: String)
52+
case class Contains(key: String)
5453
```
5554

5655
Since we are using Scala, we want to be as pure as practically
@@ -139,10 +138,9 @@ class MyActor extends Actor {
139138
def validate(key: String): Future[Boolean] = ???
140139
}
141140

142-
object MyActor {
143-
case class Add(key: String)
144-
caes class Validated(key: String, isValid: Boolean)
145-
}
141+
// Messages
142+
case class Add(key: String)
143+
case class Validated(key: String, isValid: Boolean)
146144
```
147145

148146
And of course, we could be modeling a state-machine that doesn't
@@ -180,12 +178,12 @@ class MyActor extends Actor {
180178
def validate(key: String): Future[Boolean] = ???
181179
}
182180

183-
object MyActor {
184-
case class Add(key: String)
185-
case class Validated(key: String, isValid: Boolean)
186-
case object Continue
187-
case object Rejected
188-
}
181+
// Messages
182+
183+
case class Add(key: String)
184+
case class Validated(key: String, isValid: Boolean)
185+
case object Continue
186+
case object Rejected
189187
```
190188

191189
Yeap, actor-based designs can get tricky.

0 commit comments

Comments
 (0)