Jakub Kozłowski - Scala Developer, Scalac
* - actually, 5Jakub Kozłowski - Scala Developer, Scalac
Be advised
Be pragmatic about your code
Sacrificing readability for conciseness
Sin 1 - Sacrificing readability for conciseness
Map(...).map(_._1)
No context
Map(...).map { case (name, _) => name }
Sin 1 - Sacrificing readability for conciseness
val results = elems.map(...).flatMap(...).filter(...).groupBy(_._2)...
50 characters later...
.count(...) + 5
Unclear reasoning, hard to read/debug
Create intermediate vals with meaningful names
Sin 1 - Sacrificing readability for conciseness
trait UserInfoService {
def fetchUserInfo(userId: UserId) = ...
}
Type leaks implementation details, is too concrete, eases breaking binary compatibility
def fetchUserInfo(userId: UserId): Future[Option[UserInfo]] = ???
Rule of thumb - if you aren't:
storing your code on a floppy disk
writing a tweet
writing Game of Thrones
"Programs must be written for people to read, and only incidentally for machines to execute"
Think about how much meaning your code has, not how many characters you save
Feature overuse
Sin 2 - Feature overuse
case class UserService(http: Http) {
def fetchUser(id: UserId): Future[User] = http.get(...)
}
just use apply
or new
Sin 2 - Feature overuse
val resultF = WS url "https://google.com" withHeaders (...)
withQueryString (...) get
Hard to read, postfix requires feature flag import
Sin 2 - Feature overuse
def extract(name: String): (String, String)
No context, no meaning, no type safety
def extract(name: String): (FirstName, LastName)
def extract(name: String): FirstAndLastName
Sin 2 - Feature overuse
xs.map(f)
.flatMap {
_.flatMap(g).map(h)
}
.map(i)
Nesting/chaining hell
always think about readability
use the minimal viable solution for the task at hand
follow the Principle of Least Power
The one about implicits...
Presumably the most abused Scala feature
Sin 3 - Abusing implicits
case class UserId(value: Int) extends AnyVal
implicit def uid2i(uid: UserId): Int = uid.value
implicit def i2uid(i: Int): UserId = UserId(i)
Missing the point of value classes
Explicit conversions, e.g.
10.toUserId
Sin 3 - Abusing implicits
implicit def s2i(s: String): Int = s.toInt
Can throw, brings confusion if used by accident
Sin 3 - Abusing implicits
def doSth(implicit seq: List[String])
Brings a dangerous conversion to scope
implicit val x = List("hello", "world")
val a: String = 2 // java.lang.IndexOutOfBoundsException: 2
Also makes finding the used instance harder
Monad[List]
is fine, List[HttpHeader]
is notSin 3 - Abusing implicits
def doSth(implicit seq: List[String] = List("x"))
Pure evil, makes debugging ultra hard
Implicits are a powerful feature - use it with care
Trying to outsmart the language
Sin 4 - Trying to outsmart the language
def printStuff(config: Config, mxn: MxN): String = {
var builder = StringBuilder.newBuilder
builder = builder.append("\n")
for (i <- 0 until mxn._1) {
for (y <- 0 until mxn._2) {
builder = builder.append("|")
if (config.exists(o => o._1 == (i, y))) {
config.filter(t => t._1 == (i, y)).
map(_._2).foreach(p => {
builder = builder.append(p)
})
} else {
builder = builder.append(" ")
}
}
builder = builder.append("|\n")
}
builder.toString
}
def mkString(start: String, sep: String, end: String): String = {
val b = new StringBuilder()
var first = true
b append start
for (x <- self) {
if (first) {
b append x
first = false
}
else {
b append sep
b append x
}
}
b append end
b.toString
}
Sin 4 - Trying to outsmart the language
Premature optimization
Custom wrappers over libraries
and so on
Deserves its own talk...don't reinvent the wheel for performance if you don't need it
see how the stdlib is implemented sometimes
you can't fix it if you can't understand it
Writing Java in Scala
Sin 5 - Writing Java in Scala
Boils down to writing imperative code and ignoring the type system's powerSin 5 - Writing Java in Scala
nulls, exceptions
not explicit enough, dangerous in runtime
use Option/Either/Try/Validated etc.
Sin 5 - Writing Java in Scala
mutable state, side effects
hard to reason about, makes debugging an eternal pain
limit the scope of mutability
use more explicit effect types - IO/Free or at least Future ;)
Sin 5 - Writing Java in Scala
Stubbornly sticking to OO patterns
not always applicable
Focus on FP patterns
Sin 5 - Writing Java in Scala
return
silently throws an exception, is an effect
find, fold, if/else with values in each branch, etc.
Learn the language features thoroughly
Use safe error handling techniques
Conform to the principles of FP: pure, safe code
Practice, learn, rinse and repeat
Not just Scala
Syntax and features are just the beginning
Never stop learning
Questions?
Slides: kubukoz.github.io/seven-sins-slides
Contact me: