SlideShare a Scribd company logo
Analysing Scala Puzzlers:
Essential and Accidental
Complexity in Scala
Andrew Phillips & Nermin Serifovic
@ScalaPuzzlers
About us
Andrew
● Lots of enterprise software
development on high-
performance systems
● Active open source contributor
and committer
Nermin
Co-organizer of Boston Area Scala
Enthusiasts
Co-instructor of Concurrent
Programming in Scala
Maintainers of scalapuzzlers.com and
authors of Scala Puzzlers
Agenda
Introduction
Puzzler Clusters
Reflections & Conclusions
Introduction
What are we trying to do here?
● We’ve been collecting examples of “surprising”
Scala code for a couple of years now
● We thought it was time to see if we could
identify some patterns
● Looking at the results, we tried to ask
ourselves: is the “puzzler cost” of a particular
language area outweighed by the “feature
benefit”
What are we trying to do here?
The clusters, based on 46 puzzlers:
● Cluster 1: Object-orientation
● Cluster 2: Collections
● Cluster 3: Syntax sugar
● Cluster 4: The type system
●Cluster 5: Functional programming
Cluster 1: Object-
orientation
15 puzzlers
What’s the issue?
Scala aims to combine functional and object-
oriented features...and play nicely with Java on
top of that.
Gotcha: Java peeking in
def value: Int = {
def one(x: Int): Int = { return x; 1 }
val two = (x: Int) => { return x; 2 }
1 + one(2) + two(3)
}
println(value)
Gotcha: Initialization in subclasses
class A {
type X // equivalent to X <: Any
var x: X = _
}
class B extends A { type X = Int }
val b = new B
println(b.x)
val bX = b.x
println(bX)
Gotcha: Constructors
object HipNTrendyMarket extends App { // now extends App!
implicit val normalMarkup = new Markup
Console println makeLabel(3)
implicit val touristMarkup = new TouristMarkup
Console println makeLabel(5)
}
object OldSkoolMarket {
def main(args: Array[String]): Unit = {
implicit val normalMarkup = new Markup
Console println makeLabel(3)
implicit val touristMarkup = new TouristMarkup
Console println makeLabel(5)
}
}
Moral of the story
Try to prevent non-idiomatic elements from
seeping though Scala/Java boundaries in
your code
If you’re using a lot of inheritance, study the
rules for variable initialization
Class body != method body
Cluster 2: Collections
9 puzzlers
Collections are powerful...and sometimes
puzzling
What’s the issue?
Gotcha: collections are functions!
Most commonly used collections are instances
of Function1:
List: index => element
Set: element => Boolean
Map: key => value
Gotcha: collections are functions!
This comes handy in many situations, such as:
val daysOfWeek = Map("Mon" -> 1, "Tue" -> 2, "Wed" -> 3,
"Thu" -> 4, "Fri" -> 5, "Sat" -> 6, "Sun" -> 7)
def foo(day: String, daysOfWeek: String => Int) =
println(s"${day} is the ${daysOfWeek(day)}. day of the week")
scala> foo("Mon", daysOfWeek)
Mon is the 1. day of the week
Gotcha: collections are functions!
Sometimes, this produces undesired effects...
def pad2(sb: StringBuilder, width: Int) = {
1 to width - sb.length foreach { sb append '*' }
sb
}
Gotcha: collections are functions!
Sometimes, this produces undesired effects...
def pad2(sb: StringBuilder, width: Int) = {
1 to width - sb.length foreach { sb append '*' }
sb
}
// 1 to (width - sb.length) foreach (_ => sb append '*')
Gotcha: convenience (?) methods
val ints = Map(1 -> List(1, 2, 3, 4, 5))
val bits = ints map { case (k, v) => (k, v.toIterator) }
val nits = ints mapValues (_.toIterator)
scala> print(bits(1).next, bits(1).next)
(1,2)
scala> print(nits(1).next, nits(1).next)
(1,1)
// keys are mapped to key => this(key).toIterator
Gotcha: convenience (?) methods
import collection.mutable.Queue
val goodies: Map[String, Queue[String]] = Map().withDefault(_ =>
Queue("No superheros here. Keep looking."))
val baddies: Map[String, Queue[String]] =
Map().withDefaultValue(Queue("No monsters here. Lucky you."))
println(goodies("kitchen").dequeue)
println(baddies("in attic").dequeue)
println(goodies("dining room").dequeue)
println(baddies("under bed").dequeue)
Moral of the story
Scala collections are powerful
Can be passed where Function1 is expected,
which is a useful feature, but also one to be
careful about
Pay special attention when dealing with
convenience methods that appear similar -
not all of them behave intuitively
Cluster 3:
Syntax sugar
11 puzzlers
Scala promotes elegant, concise coding style.
At the same time, the spec tries to remove
complexity from the compilation process early.
This results in a lot of rewriting of expressions.
What’s the issue?
Gotcha: placeh_lders
List(1, 2).map { i => println("Hi"); i + 1 }
List(1, 2).map { println("Hi"); _ + 1 }
Gotcha: for expressions
val xs = Seq(Seq("a", "b", "c"), Seq("d", "e", "f"), Seq("g",
"h"), Seq("i", "j", "k"))
val ys = for (Seq(x, y, z) <- xs) yield x + y + z
val zs = xs map { case Seq(x, y, z) => x + y + z }
Gotcha: implicit magic
case class Card(number: Int, suit: String = "clubs") {
val value = (number % 13) + 1 // ace = 1, king = 13
def isInDeck(implicit deck: List[Card]) = deck contains this
}
implicit val deck = List(Card(1, "clubs"))
implicit def intToCard(n: Int) = Card(n)
println(1.isInDeck)
Moral of the story
Beware when the syntax sugar for similar but
different things looks almost identical
Learn the details of more complex
desugarings, e.g. how for expressions are
translated
There are too many ways to do (almost) the
same thing with implicits - stick to one style
Cluster 4:
The Type System
7 puzzlers
By design, the Scala compiler tries to be
tolerant and “make the most of” an expression
by slightly transforming it in various ways.
In lots of situations, this goes against
programmer’s intentions
What’s the issue?
Gotcha: Type widening
val zippedLists = (List(1,3,5), List(2,4,6)).zipped
val result = zippedLists.find(_._1 > 10).getOrElse(10)
result: Any = 10
def List.find(p: (A) ⇒ Boolean): Option[A]
def Option.getOrElse[B >: A](default: ⇒ B): B
Type B is inferred to be Any - the least common
supertype between Tuple2 and Int
Gotcha: Type widening
For explicitly defined type hierarchies, this works as
intended:
scala> trait Animal
scala> class Dog extends Animal
scala> class Cat extends Animal
scala> val dog = Option(new Dog())
scala> val cat = Option(new Cat())
scala> dog.orElse(cat)
res0: Option[Animal] = Some(Dog@7d8995e)
Gotcha: Auto-tupling
def foo(any: Any) = println(any)
foo(“a”, “b”, “c”)
(a, b, c)
The compiler tries to pack all arguments into a
tuple and applies foo to that. This is the last
thing which is tried (after default arguments).
Gotcha: Type adaption galore
val numbers = List("1", "2").toSet() + "3"
println(numbers)
false3
def List.toSet[B >: A]: Set[B]
List("1", "2").toSet.apply() // apply() == contains()
Compiler implicitly inserts Unit value () and infers
supertype Any:
List("1", "2").toSet[Any].apply(())
Gotcha: Type adaption galore
●Not desirable in general
●Too much “magic” involved
●Automatic () insertion deprecated in 2.11
Moral of the story
We often wish the compiler was more strict
and threw an error in such cases
We should take advantage of static analysis
tools available (WartRemover, compiler
flags, linters, etc.)
Extensive unit testing of statically typed code
is still necessary
Moral of the story
Take advantage of the compiler flags:
-deprecation,
-unchecked,
-feature,
-Xfatal-warnings,
-Xlint
Cluster 5:
Functional programming
5 puzzlers
What’s the issue?
There are lots of ways you can specify and call
functions...
Gotcha: Partial application
var x = 0
def counter = { x += 1; x }
def add(a: Int)(b: Int) = a + b
val adder1 = add(counter)(_)
val adder2 = add(counter) _
println("x = " + x)
println(adder1(10))
println("x = " + x)
println(adder2(10))
println("x = " + x)
Gotcha: Native function syntax
val isEven = PartialFunction[Int, String] {
case n if n % 2 == 0 => "Even"
}
Moral of the story
There are many flavours of partial application
that do slightly different things - try to stick to
a few
If you are going to use native function syntax,
ensure you know exactly what you’re
creating
Reflections &
Conclusions
●Java interoperability comes at a high cost
●It pays to study the rules of common
desugarings closely
●Implicits up the level of “magic” in your code
quite significantly
●Read the ScalaDoc carefully
●Don’t skimp on unit testing
TL;DR: agree on :
● Scala features you really need; use linters,
code review etc. to catch the others
●common code style in areas where the
language allows multiple options
Questions?
Thank you!
Be the one to submit the next puzzler at
scalapuzzlers.com!
Ad

More Related Content

What's hot (20)

Property based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rulesProperty based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rules
Debasish Ghosh
 
Why Haskell
Why HaskellWhy Haskell
Why Haskell
Susan Potter
 
A Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingA Sceptical Guide to Functional Programming
A Sceptical Guide to Functional Programming
Garth Gilmour
 
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
JDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation streamJDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation stream
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
Ruslan Shevchenko
 
A Brief Intro to Scala
A Brief Intro to ScalaA Brief Intro to Scala
A Brief Intro to Scala
Tim Underwood
 
Scala introduction
Scala introductionScala introduction
Scala introduction
vito jeng
 
Scala fundamentals
Scala fundamentalsScala fundamentals
Scala fundamentals
Alfonso Ruzafa
 
A Tour Of Scala
A Tour Of ScalaA Tour Of Scala
A Tour Of Scala
fanf42
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar Prokopec
Loïc Descotte
 
Joy of scala
Joy of scalaJoy of scala
Joy of scala
Maxim Novak
 
Introduction to scala
Introduction to scalaIntroduction to scala
Introduction to scala
Michel Perez
 
Scala for curious
Scala for curiousScala for curious
Scala for curious
Tim (dev-tim) Zadorozhniy
 
Scala collections api expressivity and brevity upgrade from java
Scala collections api  expressivity and brevity upgrade from javaScala collections api  expressivity and brevity upgrade from java
Scala collections api expressivity and brevity upgrade from java
IndicThreads
 
Demystifying functional programming with Scala
Demystifying functional programming with ScalaDemystifying functional programming with Scala
Demystifying functional programming with Scala
Denis
 
Scala vs java 8
Scala vs java 8Scala vs java 8
Scala vs java 8
François Sarradin
 
ScalaDays 2013 Keynote Speech by Martin Odersky
ScalaDays 2013 Keynote Speech by Martin OderskyScalaDays 2013 Keynote Speech by Martin Odersky
ScalaDays 2013 Keynote Speech by Martin Odersky
Typesafe
 
Scala on Android
Scala on AndroidScala on Android
Scala on Android
Jakub Kahovec
 
scala.reflect, Eugene Burmako
scala.reflect, Eugene Burmakoscala.reflect, Eugene Burmako
scala.reflect, Eugene Burmako
Vasil Remeniuk
 
Scala introduction
Scala introductionScala introduction
Scala introduction
Yardena Meymann
 
Metaprogramming in Scala 2.10, Eugene Burmako,
Metaprogramming  in Scala 2.10, Eugene Burmako, Metaprogramming  in Scala 2.10, Eugene Burmako,
Metaprogramming in Scala 2.10, Eugene Burmako,
Vasil Remeniuk
 
Property based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rulesProperty based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rules
Debasish Ghosh
 
A Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingA Sceptical Guide to Functional Programming
A Sceptical Guide to Functional Programming
Garth Gilmour
 
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
JDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation streamJDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation stream
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
Ruslan Shevchenko
 
A Brief Intro to Scala
A Brief Intro to ScalaA Brief Intro to Scala
A Brief Intro to Scala
Tim Underwood
 
Scala introduction
Scala introductionScala introduction
Scala introduction
vito jeng
 
A Tour Of Scala
A Tour Of ScalaA Tour Of Scala
A Tour Of Scala
fanf42
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar Prokopec
Loïc Descotte
 
Introduction to scala
Introduction to scalaIntroduction to scala
Introduction to scala
Michel Perez
 
Scala collections api expressivity and brevity upgrade from java
Scala collections api  expressivity and brevity upgrade from javaScala collections api  expressivity and brevity upgrade from java
Scala collections api expressivity and brevity upgrade from java
IndicThreads
 
Demystifying functional programming with Scala
Demystifying functional programming with ScalaDemystifying functional programming with Scala
Demystifying functional programming with Scala
Denis
 
ScalaDays 2013 Keynote Speech by Martin Odersky
ScalaDays 2013 Keynote Speech by Martin OderskyScalaDays 2013 Keynote Speech by Martin Odersky
ScalaDays 2013 Keynote Speech by Martin Odersky
Typesafe
 
scala.reflect, Eugene Burmako
scala.reflect, Eugene Burmakoscala.reflect, Eugene Burmako
scala.reflect, Eugene Burmako
Vasil Remeniuk
 
Metaprogramming in Scala 2.10, Eugene Burmako,
Metaprogramming  in Scala 2.10, Eugene Burmako, Metaprogramming  in Scala 2.10, Eugene Burmako,
Metaprogramming in Scala 2.10, Eugene Burmako,
Vasil Remeniuk
 

Viewers also liked (17)

Valentine's Day Wine Menu
Valentine's Day Wine MenuValentine's Day Wine Menu
Valentine's Day Wine Menu
Laura Bull
 
Watching wrc 2015 rally poland live
Watching wrc 2015 rally poland liveWatching wrc 2015 rally poland live
Watching wrc 2015 rally poland live
romannable
 
Wrc 2015 rally poland
Wrc 2015 rally polandWrc 2015 rally poland
Wrc 2015 rally poland
romannable
 
Desarrollo sustentable
Desarrollo sustentableDesarrollo sustentable
Desarrollo sustentable
alejandro rangel
 
Deployment is the new build
Deployment is the new buildDeployment is the new build
Deployment is the new build
Andrew Phillips
 
My role
My roleMy role
My role
nkadzi elvis
 
mapa-conceptual-actividad1-grupo6
mapa-conceptual-actividad1-grupo6mapa-conceptual-actividad1-grupo6
mapa-conceptual-actividad1-grupo6
yoneireht paola briceño perez
 
Diagrama Slideshare
Diagrama SlideshareDiagrama Slideshare
Diagrama Slideshare
yoani19
 
Fenomenosydesastresnaturales final 1.0
Fenomenosydesastresnaturales final 1.0Fenomenosydesastresnaturales final 1.0
Fenomenosydesastresnaturales final 1.0
christian pablo vicente bedoya
 
Kelopok kursi amer
Kelopok kursi amerKelopok kursi amer
Kelopok kursi amer
NICO AKBAR
 
Las políticas alimentarias
Las políticas alimentariasLas políticas alimentarias
Las políticas alimentarias
Eduardo Galaviz
 
Metrics-driven Continuous Delivery
Metrics-driven Continuous DeliveryMetrics-driven Continuous Delivery
Metrics-driven Continuous Delivery
Andrew Phillips
 
Comunicación 1.0 y 2.0: Aplicación en el ámbito de la SST
Comunicación 1.0 y 2.0: Aplicación en el ámbito de la SSTComunicación 1.0 y 2.0: Aplicación en el ámbito de la SST
Comunicación 1.0 y 2.0: Aplicación en el ámbito de la SST
Community Manager Full Audit
 
Know your cirrus from your cumulus (with notes)
Know your cirrus from your cumulus (with notes)Know your cirrus from your cumulus (with notes)
Know your cirrus from your cumulus (with notes)
Andrew Phillips
 
Mapa conceptual proyectos
Mapa conceptual proyectosMapa conceptual proyectos
Mapa conceptual proyectos
yoneireht paola briceño perez
 
Ad

Similar to Scala Up North: "Analysing Scala Puzzlers: Essential and Accidental Complexity in Scala" (20)

Functional Programming With Scala
Functional Programming With ScalaFunctional Programming With Scala
Functional Programming With Scala
Knoldus Inc.
 
Functional programming with Scala
Functional programming with ScalaFunctional programming with Scala
Functional programming with Scala
Neelkanth Sachdeva
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scala
shinolajla
 
Scala for Java Programmers
Scala for Java ProgrammersScala for Java Programmers
Scala for Java Programmers
Eric Pederson
 
Scala Talk at FOSDEM 2009
Scala Talk at FOSDEM 2009Scala Talk at FOSDEM 2009
Scala Talk at FOSDEM 2009
Martin Odersky
 
Scala ntnu
Scala ntnuScala ntnu
Scala ntnu
Alf Kristian Støyle
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
SeniorDevOnly
 
Scala @ TechMeetup Edinburgh
Scala @ TechMeetup EdinburghScala @ TechMeetup Edinburgh
Scala @ TechMeetup Edinburgh
Stuart Roebuck
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
Stuart Roebuck
 
The things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaThe things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and Akka
Konrad Malawski
 
Scala final ppt vinay
Scala final ppt vinayScala final ppt vinay
Scala final ppt vinay
Viplav Jain
 
The Scala Programming Language
The Scala Programming LanguageThe Scala Programming Language
The Scala Programming Language
league
 
Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)
Tomer Gabel
 
Fancy talk
Fancy talkFancy talk
Fancy talk
nnnnnnnnnnn lllllllll
 
apache spark presentation for distributed processing
apache spark presentation for distributed processingapache spark presentation for distributed processing
apache spark presentation for distributed processing
iamdrnaeem
 
Introduction to meta-programming in scala
Introduction to meta-programming in scalaIntroduction to meta-programming in scala
Introduction to meta-programming in scala
Alessandro Marrella
 
Functional Programming With Scala
Functional Programming With ScalaFunctional Programming With Scala
Functional Programming With Scala
Knoldus Inc.
 
Functional programming with Scala
Functional programming with ScalaFunctional programming with Scala
Functional programming with Scala
Neelkanth Sachdeva
 
Taxonomy of Scala
Taxonomy of ScalaTaxonomy of Scala
Taxonomy of Scala
shinolajla
 
Scala for Java Programmers
Scala for Java ProgrammersScala for Java Programmers
Scala for Java Programmers
Eric Pederson
 
Scala Talk at FOSDEM 2009
Scala Talk at FOSDEM 2009Scala Talk at FOSDEM 2009
Scala Talk at FOSDEM 2009
Martin Odersky
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdf
Hiroshi Ono
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
SeniorDevOnly
 
Scala @ TechMeetup Edinburgh
Scala @ TechMeetup EdinburghScala @ TechMeetup Edinburgh
Scala @ TechMeetup Edinburgh
Stuart Roebuck
 
The things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaThe things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and Akka
Konrad Malawski
 
Scala final ppt vinay
Scala final ppt vinayScala final ppt vinay
Scala final ppt vinay
Viplav Jain
 
The Scala Programming Language
The Scala Programming LanguageThe Scala Programming Language
The Scala Programming Language
league
 
Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)Speaking Scala: Refactoring for Fun and Profit (Workshop)
Speaking Scala: Refactoring for Fun and Profit (Workshop)
Tomer Gabel
 
apache spark presentation for distributed processing
apache spark presentation for distributed processingapache spark presentation for distributed processing
apache spark presentation for distributed processing
iamdrnaeem
 
Introduction to meta-programming in scala
Introduction to meta-programming in scalaIntroduction to meta-programming in scala
Introduction to meta-programming in scala
Alessandro Marrella
 
Ad

More from Andrew Phillips (11)

Spinnaker Summit 2019: Where are we heading? The Future of Continuous Delivery
Spinnaker Summit 2019: Where are we heading? The Future of Continuous DeliverySpinnaker Summit 2019: Where are we heading? The Future of Continuous Delivery
Spinnaker Summit 2019: Where are we heading? The Future of Continuous Delivery
Andrew Phillips
 
Docker New York City: From GitOps to a scalable CI/CD Pattern for Kubernetes
Docker New York City: From GitOps to a scalable CI/CD Pattern for KubernetesDocker New York City: From GitOps to a scalable CI/CD Pattern for Kubernetes
Docker New York City: From GitOps to a scalable CI/CD Pattern for Kubernetes
Andrew Phillips
 
Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...
Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...
Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...
Andrew Phillips
 
Spinnaker Summit 2018: CI/CD Patterns for Kubernetes with Spinnaker
Spinnaker Summit 2018: CI/CD Patterns for Kubernetes with SpinnakerSpinnaker Summit 2018: CI/CD Patterns for Kubernetes with Spinnaker
Spinnaker Summit 2018: CI/CD Patterns for Kubernetes with Spinnaker
Andrew Phillips
 
OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...
OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...
OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...
Andrew Phillips
 
New York Kubernetes: CI/CD Patterns for Kubernetes
New York Kubernetes: CI/CD Patterns for KubernetesNew York Kubernetes: CI/CD Patterns for Kubernetes
New York Kubernetes: CI/CD Patterns for Kubernetes
Andrew Phillips
 
nycdevops: "Breaking Down the Prod/Dev Wall"
nycdevops: "Breaking Down the Prod/Dev Wall"nycdevops: "Breaking Down the Prod/Dev Wall"
nycdevops: "Breaking Down the Prod/Dev Wall"
Andrew Phillips
 
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
Andrew Phillips
 
The Multiple Dimensions of Cross-Cloud Computing
The Multiple Dimensions of Cross-Cloud ComputingThe Multiple Dimensions of Cross-Cloud Computing
The Multiple Dimensions of Cross-Cloud Computing
Andrew Phillips
 
Implementing Continuous Deployment
Implementing Continuous DeploymentImplementing Continuous Deployment
Implementing Continuous Deployment
Andrew Phillips
 
Know your cirrus from your cumulus
Know your cirrus from your cumulusKnow your cirrus from your cumulus
Know your cirrus from your cumulus
Andrew Phillips
 
Spinnaker Summit 2019: Where are we heading? The Future of Continuous Delivery
Spinnaker Summit 2019: Where are we heading? The Future of Continuous DeliverySpinnaker Summit 2019: Where are we heading? The Future of Continuous Delivery
Spinnaker Summit 2019: Where are we heading? The Future of Continuous Delivery
Andrew Phillips
 
Docker New York City: From GitOps to a scalable CI/CD Pattern for Kubernetes
Docker New York City: From GitOps to a scalable CI/CD Pattern for KubernetesDocker New York City: From GitOps to a scalable CI/CD Pattern for Kubernetes
Docker New York City: From GitOps to a scalable CI/CD Pattern for Kubernetes
Andrew Phillips
 
Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...
Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...
Continuous Delivery NYC: From GitOps to an adaptable CI/CD Pattern for Kubern...
Andrew Phillips
 
Spinnaker Summit 2018: CI/CD Patterns for Kubernetes with Spinnaker
Spinnaker Summit 2018: CI/CD Patterns for Kubernetes with SpinnakerSpinnaker Summit 2018: CI/CD Patterns for Kubernetes with Spinnaker
Spinnaker Summit 2018: CI/CD Patterns for Kubernetes with Spinnaker
Andrew Phillips
 
OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...
OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...
OpenDev 2018: "Open CD for Open Infrastructure - Hybrid and Multi-Cloud Deplo...
Andrew Phillips
 
New York Kubernetes: CI/CD Patterns for Kubernetes
New York Kubernetes: CI/CD Patterns for KubernetesNew York Kubernetes: CI/CD Patterns for Kubernetes
New York Kubernetes: CI/CD Patterns for Kubernetes
Andrew Phillips
 
nycdevops: "Breaking Down the Prod/Dev Wall"
nycdevops: "Breaking Down the Prod/Dev Wall"nycdevops: "Breaking Down the Prod/Dev Wall"
nycdevops: "Breaking Down the Prod/Dev Wall"
Andrew Phillips
 
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
Andrew Phillips
 
The Multiple Dimensions of Cross-Cloud Computing
The Multiple Dimensions of Cross-Cloud ComputingThe Multiple Dimensions of Cross-Cloud Computing
The Multiple Dimensions of Cross-Cloud Computing
Andrew Phillips
 
Implementing Continuous Deployment
Implementing Continuous DeploymentImplementing Continuous Deployment
Implementing Continuous Deployment
Andrew Phillips
 
Know your cirrus from your cumulus
Know your cirrus from your cumulusKnow your cirrus from your cumulus
Know your cirrus from your cumulus
Andrew Phillips
 

Recently uploaded (20)

DevOpsDays SLC - Platform Engineers are Product Managers.pptx
DevOpsDays SLC - Platform Engineers are Product Managers.pptxDevOpsDays SLC - Platform Engineers are Product Managers.pptx
DevOpsDays SLC - Platform Engineers are Product Managers.pptx
Justin Reock
 
Top 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptx
Top 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptxTop 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptx
Top 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptx
mkubeusa
 
Agentic Automation - Delhi UiPath Community Meetup
Agentic Automation - Delhi UiPath Community MeetupAgentic Automation - Delhi UiPath Community Meetup
Agentic Automation - Delhi UiPath Community Meetup
Manoj Batra (1600 + Connections)
 
Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...
Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...
Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...
Maarten Verwaest
 
Slack like a pro: strategies for 10x engineering teams
Slack like a pro: strategies for 10x engineering teamsSlack like a pro: strategies for 10x engineering teams
Slack like a pro: strategies for 10x engineering teams
Nacho Cougil
 
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
SOFTTECHHUB
 
Unlocking Generative AI in your Web Apps
Unlocking Generative AI in your Web AppsUnlocking Generative AI in your Web Apps
Unlocking Generative AI in your Web Apps
Maximiliano Firtman
 
AsyncAPI v3 : Streamlining Event-Driven API Design
AsyncAPI v3 : Streamlining Event-Driven API DesignAsyncAPI v3 : Streamlining Event-Driven API Design
AsyncAPI v3 : Streamlining Event-Driven API Design
leonid54
 
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptxReimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
John Moore
 
Smart Investments Leveraging Agentic AI for Real Estate Success.pptx
Smart Investments Leveraging Agentic AI for Real Estate Success.pptxSmart Investments Leveraging Agentic AI for Real Estate Success.pptx
Smart Investments Leveraging Agentic AI for Real Estate Success.pptx
Seasia Infotech
 
Artificial_Intelligence_in_Everyday_Life.pptx
Artificial_Intelligence_in_Everyday_Life.pptxArtificial_Intelligence_in_Everyday_Life.pptx
Artificial_Intelligence_in_Everyday_Life.pptx
03ANMOLCHAURASIYA
 
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Raffi Khatchadourian
 
Shoehorning dependency injection into a FP language, what does it take?
Shoehorning dependency injection into a FP language, what does it take?Shoehorning dependency injection into a FP language, what does it take?
Shoehorning dependency injection into a FP language, what does it take?
Eric Torreborre
 
Com fer un pla de gestió de dades amb l'eiNa DMP (en anglès)
Com fer un pla de gestió de dades amb l'eiNa DMP (en anglès)Com fer un pla de gestió de dades amb l'eiNa DMP (en anglès)
Com fer un pla de gestió de dades amb l'eiNa DMP (en anglès)
CSUC - Consorci de Serveis Universitaris de Catalunya
 
Kit-Works Team Study_아직도 Dockefile.pdf_김성호
Kit-Works Team Study_아직도 Dockefile.pdf_김성호Kit-Works Team Study_아직도 Dockefile.pdf_김성호
Kit-Works Team Study_아직도 Dockefile.pdf_김성호
Wonjun Hwang
 
Developing System Infrastructure Design Plan.pptx
Developing System Infrastructure Design Plan.pptxDeveloping System Infrastructure Design Plan.pptx
Developing System Infrastructure Design Plan.pptx
wondimagegndesta
 
RTP Over QUIC: An Interesting Opportunity Or Wasted Time?
RTP Over QUIC: An Interesting Opportunity Or Wasted Time?RTP Over QUIC: An Interesting Opportunity Or Wasted Time?
RTP Over QUIC: An Interesting Opportunity Or Wasted Time?
Lorenzo Miniero
 
Cybersecurity Threat Vectors and Mitigation
Cybersecurity Threat Vectors and MitigationCybersecurity Threat Vectors and Mitigation
Cybersecurity Threat Vectors and Mitigation
VICTOR MAESTRE RAMIREZ
 
An Overview of Salesforce Health Cloud & How is it Transforming Patient Care
An Overview of Salesforce Health Cloud & How is it Transforming Patient CareAn Overview of Salesforce Health Cloud & How is it Transforming Patient Care
An Overview of Salesforce Health Cloud & How is it Transforming Patient Care
Cyntexa
 
Building the Customer Identity Community, Together.pdf
Building the Customer Identity Community, Together.pdfBuilding the Customer Identity Community, Together.pdf
Building the Customer Identity Community, Together.pdf
Cheryl Hung
 
DevOpsDays SLC - Platform Engineers are Product Managers.pptx
DevOpsDays SLC - Platform Engineers are Product Managers.pptxDevOpsDays SLC - Platform Engineers are Product Managers.pptx
DevOpsDays SLC - Platform Engineers are Product Managers.pptx
Justin Reock
 
Top 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptx
Top 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptxTop 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptx
Top 5 Benefits of Using Molybdenum Rods in Industrial Applications.pptx
mkubeusa
 
Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...
Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...
Limecraft Webinar - 2025.3 release, featuring Content Delivery, Graphic Conte...
Maarten Verwaest
 
Slack like a pro: strategies for 10x engineering teams
Slack like a pro: strategies for 10x engineering teamsSlack like a pro: strategies for 10x engineering teams
Slack like a pro: strategies for 10x engineering teams
Nacho Cougil
 
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
The No-Code Way to Build a Marketing Team with One AI Agent (Download the n8n...
SOFTTECHHUB
 
Unlocking Generative AI in your Web Apps
Unlocking Generative AI in your Web AppsUnlocking Generative AI in your Web Apps
Unlocking Generative AI in your Web Apps
Maximiliano Firtman
 
AsyncAPI v3 : Streamlining Event-Driven API Design
AsyncAPI v3 : Streamlining Event-Driven API DesignAsyncAPI v3 : Streamlining Event-Driven API Design
AsyncAPI v3 : Streamlining Event-Driven API Design
leonid54
 
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptxReimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
Reimagine How You and Your Team Work with Microsoft 365 Copilot.pptx
John Moore
 
Smart Investments Leveraging Agentic AI for Real Estate Success.pptx
Smart Investments Leveraging Agentic AI for Real Estate Success.pptxSmart Investments Leveraging Agentic AI for Real Estate Success.pptx
Smart Investments Leveraging Agentic AI for Real Estate Success.pptx
Seasia Infotech
 
Artificial_Intelligence_in_Everyday_Life.pptx
Artificial_Intelligence_in_Everyday_Life.pptxArtificial_Intelligence_in_Everyday_Life.pptx
Artificial_Intelligence_in_Everyday_Life.pptx
03ANMOLCHAURASIYA
 
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Challenges in Migrating Imperative Deep Learning Programs to Graph Execution:...
Raffi Khatchadourian
 
Shoehorning dependency injection into a FP language, what does it take?
Shoehorning dependency injection into a FP language, what does it take?Shoehorning dependency injection into a FP language, what does it take?
Shoehorning dependency injection into a FP language, what does it take?
Eric Torreborre
 
Kit-Works Team Study_아직도 Dockefile.pdf_김성호
Kit-Works Team Study_아직도 Dockefile.pdf_김성호Kit-Works Team Study_아직도 Dockefile.pdf_김성호
Kit-Works Team Study_아직도 Dockefile.pdf_김성호
Wonjun Hwang
 
Developing System Infrastructure Design Plan.pptx
Developing System Infrastructure Design Plan.pptxDeveloping System Infrastructure Design Plan.pptx
Developing System Infrastructure Design Plan.pptx
wondimagegndesta
 
RTP Over QUIC: An Interesting Opportunity Or Wasted Time?
RTP Over QUIC: An Interesting Opportunity Or Wasted Time?RTP Over QUIC: An Interesting Opportunity Or Wasted Time?
RTP Over QUIC: An Interesting Opportunity Or Wasted Time?
Lorenzo Miniero
 
Cybersecurity Threat Vectors and Mitigation
Cybersecurity Threat Vectors and MitigationCybersecurity Threat Vectors and Mitigation
Cybersecurity Threat Vectors and Mitigation
VICTOR MAESTRE RAMIREZ
 
An Overview of Salesforce Health Cloud & How is it Transforming Patient Care
An Overview of Salesforce Health Cloud & How is it Transforming Patient CareAn Overview of Salesforce Health Cloud & How is it Transforming Patient Care
An Overview of Salesforce Health Cloud & How is it Transforming Patient Care
Cyntexa
 
Building the Customer Identity Community, Together.pdf
Building the Customer Identity Community, Together.pdfBuilding the Customer Identity Community, Together.pdf
Building the Customer Identity Community, Together.pdf
Cheryl Hung
 

Scala Up North: "Analysing Scala Puzzlers: Essential and Accidental Complexity in Scala"

  • 1. Analysing Scala Puzzlers: Essential and Accidental Complexity in Scala Andrew Phillips & Nermin Serifovic @ScalaPuzzlers
  • 2. About us Andrew ● Lots of enterprise software development on high- performance systems ● Active open source contributor and committer Nermin Co-organizer of Boston Area Scala Enthusiasts Co-instructor of Concurrent Programming in Scala Maintainers of scalapuzzlers.com and authors of Scala Puzzlers
  • 5. What are we trying to do here? ● We’ve been collecting examples of “surprising” Scala code for a couple of years now ● We thought it was time to see if we could identify some patterns ● Looking at the results, we tried to ask ourselves: is the “puzzler cost” of a particular language area outweighed by the “feature benefit”
  • 6. What are we trying to do here? The clusters, based on 46 puzzlers: ● Cluster 1: Object-orientation ● Cluster 2: Collections ● Cluster 3: Syntax sugar ● Cluster 4: The type system ●Cluster 5: Functional programming
  • 8. What’s the issue? Scala aims to combine functional and object- oriented features...and play nicely with Java on top of that.
  • 9. Gotcha: Java peeking in def value: Int = { def one(x: Int): Int = { return x; 1 } val two = (x: Int) => { return x; 2 } 1 + one(2) + two(3) } println(value)
  • 10. Gotcha: Initialization in subclasses class A { type X // equivalent to X <: Any var x: X = _ } class B extends A { type X = Int } val b = new B println(b.x) val bX = b.x println(bX)
  • 11. Gotcha: Constructors object HipNTrendyMarket extends App { // now extends App! implicit val normalMarkup = new Markup Console println makeLabel(3) implicit val touristMarkup = new TouristMarkup Console println makeLabel(5) } object OldSkoolMarket { def main(args: Array[String]): Unit = { implicit val normalMarkup = new Markup Console println makeLabel(3) implicit val touristMarkup = new TouristMarkup Console println makeLabel(5) } }
  • 12. Moral of the story Try to prevent non-idiomatic elements from seeping though Scala/Java boundaries in your code If you’re using a lot of inheritance, study the rules for variable initialization Class body != method body
  • 14. Collections are powerful...and sometimes puzzling What’s the issue?
  • 15. Gotcha: collections are functions! Most commonly used collections are instances of Function1: List: index => element Set: element => Boolean Map: key => value
  • 16. Gotcha: collections are functions! This comes handy in many situations, such as: val daysOfWeek = Map("Mon" -> 1, "Tue" -> 2, "Wed" -> 3, "Thu" -> 4, "Fri" -> 5, "Sat" -> 6, "Sun" -> 7) def foo(day: String, daysOfWeek: String => Int) = println(s"${day} is the ${daysOfWeek(day)}. day of the week") scala> foo("Mon", daysOfWeek) Mon is the 1. day of the week
  • 17. Gotcha: collections are functions! Sometimes, this produces undesired effects... def pad2(sb: StringBuilder, width: Int) = { 1 to width - sb.length foreach { sb append '*' } sb }
  • 18. Gotcha: collections are functions! Sometimes, this produces undesired effects... def pad2(sb: StringBuilder, width: Int) = { 1 to width - sb.length foreach { sb append '*' } sb } // 1 to (width - sb.length) foreach (_ => sb append '*')
  • 19. Gotcha: convenience (?) methods val ints = Map(1 -> List(1, 2, 3, 4, 5)) val bits = ints map { case (k, v) => (k, v.toIterator) } val nits = ints mapValues (_.toIterator) scala> print(bits(1).next, bits(1).next) (1,2) scala> print(nits(1).next, nits(1).next) (1,1) // keys are mapped to key => this(key).toIterator
  • 20. Gotcha: convenience (?) methods import collection.mutable.Queue val goodies: Map[String, Queue[String]] = Map().withDefault(_ => Queue("No superheros here. Keep looking.")) val baddies: Map[String, Queue[String]] = Map().withDefaultValue(Queue("No monsters here. Lucky you.")) println(goodies("kitchen").dequeue) println(baddies("in attic").dequeue) println(goodies("dining room").dequeue) println(baddies("under bed").dequeue)
  • 21. Moral of the story Scala collections are powerful Can be passed where Function1 is expected, which is a useful feature, but also one to be careful about Pay special attention when dealing with convenience methods that appear similar - not all of them behave intuitively
  • 23. Scala promotes elegant, concise coding style. At the same time, the spec tries to remove complexity from the compilation process early. This results in a lot of rewriting of expressions. What’s the issue?
  • 24. Gotcha: placeh_lders List(1, 2).map { i => println("Hi"); i + 1 } List(1, 2).map { println("Hi"); _ + 1 }
  • 25. Gotcha: for expressions val xs = Seq(Seq("a", "b", "c"), Seq("d", "e", "f"), Seq("g", "h"), Seq("i", "j", "k")) val ys = for (Seq(x, y, z) <- xs) yield x + y + z val zs = xs map { case Seq(x, y, z) => x + y + z }
  • 26. Gotcha: implicit magic case class Card(number: Int, suit: String = "clubs") { val value = (number % 13) + 1 // ace = 1, king = 13 def isInDeck(implicit deck: List[Card]) = deck contains this } implicit val deck = List(Card(1, "clubs")) implicit def intToCard(n: Int) = Card(n) println(1.isInDeck)
  • 27. Moral of the story Beware when the syntax sugar for similar but different things looks almost identical Learn the details of more complex desugarings, e.g. how for expressions are translated There are too many ways to do (almost) the same thing with implicits - stick to one style
  • 28. Cluster 4: The Type System 7 puzzlers
  • 29. By design, the Scala compiler tries to be tolerant and “make the most of” an expression by slightly transforming it in various ways. In lots of situations, this goes against programmer’s intentions What’s the issue?
  • 30. Gotcha: Type widening val zippedLists = (List(1,3,5), List(2,4,6)).zipped val result = zippedLists.find(_._1 > 10).getOrElse(10) result: Any = 10 def List.find(p: (A) ⇒ Boolean): Option[A] def Option.getOrElse[B >: A](default: ⇒ B): B Type B is inferred to be Any - the least common supertype between Tuple2 and Int
  • 31. Gotcha: Type widening For explicitly defined type hierarchies, this works as intended: scala> trait Animal scala> class Dog extends Animal scala> class Cat extends Animal scala> val dog = Option(new Dog()) scala> val cat = Option(new Cat()) scala> dog.orElse(cat) res0: Option[Animal] = Some(Dog@7d8995e)
  • 32. Gotcha: Auto-tupling def foo(any: Any) = println(any) foo(“a”, “b”, “c”) (a, b, c) The compiler tries to pack all arguments into a tuple and applies foo to that. This is the last thing which is tried (after default arguments).
  • 33. Gotcha: Type adaption galore val numbers = List("1", "2").toSet() + "3" println(numbers) false3 def List.toSet[B >: A]: Set[B] List("1", "2").toSet.apply() // apply() == contains() Compiler implicitly inserts Unit value () and infers supertype Any: List("1", "2").toSet[Any].apply(())
  • 34. Gotcha: Type adaption galore ●Not desirable in general ●Too much “magic” involved ●Automatic () insertion deprecated in 2.11
  • 35. Moral of the story We often wish the compiler was more strict and threw an error in such cases We should take advantage of static analysis tools available (WartRemover, compiler flags, linters, etc.) Extensive unit testing of statically typed code is still necessary
  • 36. Moral of the story Take advantage of the compiler flags: -deprecation, -unchecked, -feature, -Xfatal-warnings, -Xlint
  • 38. What’s the issue? There are lots of ways you can specify and call functions...
  • 39. Gotcha: Partial application var x = 0 def counter = { x += 1; x } def add(a: Int)(b: Int) = a + b val adder1 = add(counter)(_) val adder2 = add(counter) _ println("x = " + x) println(adder1(10)) println("x = " + x) println(adder2(10)) println("x = " + x)
  • 40. Gotcha: Native function syntax val isEven = PartialFunction[Int, String] { case n if n % 2 == 0 => "Even" }
  • 41. Moral of the story There are many flavours of partial application that do slightly different things - try to stick to a few If you are going to use native function syntax, ensure you know exactly what you’re creating
  • 43. ●Java interoperability comes at a high cost ●It pays to study the rules of common desugarings closely ●Implicits up the level of “magic” in your code quite significantly ●Read the ScalaDoc carefully ●Don’t skimp on unit testing
  • 44. TL;DR: agree on : ● Scala features you really need; use linters, code review etc. to catch the others ●common code style in areas where the language allows multiple options
  • 46. Thank you! Be the one to submit the next puzzler at scalapuzzlers.com!
  翻译: