Discover some of bad practices in Scala and how to avoid them.
This talk is mainly about functionnal programming style but only with very simple concepts.
Hello !
Badr Baddou
Scala dev @ Zeenea
@GraalSeeker
Loïc Knuchel
Tech lead @ Zeenea
@loicknuchel
Scala
FP
DDD
Property based testing
Your next data catalog
#Data
#Documentation
#Glossary
#Lineage
#Gouvernance
@GraalSeeker
@loicknuchel
Common bad practices
● Do not use null and throw
● Avoid isEmpty / get
● Ban symbols in function names
● Pay attention of primitive types use
● Do not let library conventions leak in your code
● DRY overuse
● Beware of mixing paradigms
@GraalSeeker
@loicknuchel
Primitive
obsession
● Primitive types: Int, String, Boolean…
● They do not carry semantics
○ Int, String vs FileSize, Bandwidth, RelPath, Email…
● They are indistinguishable
○ String <=> String vs UserId <!> CommentId
● You don’t known if they were validated
○ String/String vs Email/CheckedEmail
● You have no helper methods
○ path.split(“/”).dropRight(1).mkString(“/”) vs path.getParent()
@GraalSeeker
@loicknuchel
Library-
splaining
Write a wrapper to protect from library conventions
● Anti corruption layer (DDD)
● Do types conversions
○ null <=> Option
○ throw <=> Try, Either
○ Java collections <=> Scala collections
○ primitive types <=> dedicated types => Type Safety o/
○ Any <=> ADT
● Paradigm shifts
○ mutable <=> immutable
○ currying
○ sync <=> async (dedicated thread pool)
@GraalSeeker
@loicknuchel
Solution
val tuple: (Int, Int) =
Map("id" -> ("badr", 13))
.flatMap(e => List(e._2))
.map(_._2 + 1)
./:((0, 0))((a, b) => (a._1 - 1, a._2 + b))
case class User(name: String, age: Int)
val tuple2: (Int, Int) =
Map("id" -> User("badr", 13))
.flatMap { case (_, value) => List(value) }
.map(user => user.age + 1)
.foldLeft((0, 0))((acc, e) => (acc._1 - 1, acc._2 + e))
“The most common things you want to do should be the most concise,
while the less common things should be more verbose”
Lihayo
No symbols
Use class
not tuples
Named pattern matching
Named value
@GraalSeeker
@loicknuchel
Powerful
Feature
misuse
final case class Arc(name: String)
def main() {
Arc("zzz").show()
}
case class RichArc(value: Arc) {
def show() = s"${value.name} is a rich Arc"
}
implicit def richArc(arc: Arc): RichArc = RichArc(arc)
// Problem conversion can apply when not needed
def createRichArc(): RichArc = {
// conversion not needed here
Arc("aaa")
}
@GraalSeeker
@loicknuchel
hide and seek
implicits
object App {
def main(args: Array[String]): Unit = {
val p: PropertyValue = 1
val q: PropertyValue = true
val r: PropertyValue = "toto"
}
}
package object propertyHelpers {
implicit def build(v: Any): PropertyValue =
PropertyValue(v)
}
// there should be some implicits here
// but no import
// where to look???
“Package objects can contain arbitrary definitions.
For instance, they are frequently used to hold
package-wide implicit conversions.”
https://docs.scala-lang.org/tour/package-objects.html