A talk given at Scala Matsuri 2016 in Tokyo, Japan.
New Scala practitioners often experience the uncomfortable feeling of "not quite getting it." If you've studied the syntax and written tests, maybe production code; if you're becoming comfortable with the language and libraries, but keep worrying that there's "a better way", or that your code isn't "idiomatic enough" - this session is for you.
By refactoring a real, live codebase, this talk will provide you with new tools and increased confidence. Between the provided examples and the ensuing discussion, you will walk away with a better feel for Scala and how to employ it in the real world.
2. Agenda
• For the next 40
minutes, we’ll:
– Look at examples
– Discuss patterns
– … and anti-patterns
– Showcase refactoring
techniques
例を通してパターンとアンチパターンを議論し、
リファクタリングのテクニックを説明する
3. Our Victim
• … is ScalaChess
– Provides a full domain
model for chess
– Good test coverage
– High quality code
– MIT license
– Integrated in lichess.org* ScalaChess is an open-source project by Thibault Duplessis
チェスのドメインモデルを備えるScalaChessを例にする
高品質でlichess.orgと統合されたMITライセンスのOSS
5. Stringly Typed
“Used to describe an implementation
that needlessly relies on strings when
programmer & refactor friendly options
are available.”
-- Coding
Horror
アンチパターン:Stringly Typed
型付けできる所で不必要に文字列に頼った実装のこと
6. Stringly Typed
• Examples:
– Carrying unparsed data around
– Using empty strings instead of Options
case class Person(name: String, location: String)
def nearest(to: Person, all: List[Person]): Person = {
val geo: Point = Point.parse(to.location)
all.minBy(p => geo.distanceTo(Point.parse(p.location)))
}
1. Inefficient (space/time)
2. Error handling all over the place
3. What’s with all the boilerplate?
Stringly Typed の例1:
パースしてないデータを持ち回す
7. Stringly Typed
• Examples:
– Carrying unparsed data around
– Using empty strings instead of Options
case class Person(name: String, location: Point)
def nearest(to: Person, all: List[Person]): Person =
all.minBy(p => to.location distanceTo p.location)
1. Efficient (only parsed once)
2. Sane error handling
3. Zero boilerplate!
パース後のデータを使うことで効率的で、
エラー処理が容易に、お決まりの処理も不要になる
8. Stringly Typed
• Examples:
– Carrying unparsed data around
– Using empty strings instead of Options
case class Person(firstName: String, lastName: String)
def render(p: Person): String =
s"""
|<div id='first-name'>${p.firstName}</div>
|<div id='last-name'>${p.lastName}</div>
""".stripMargin
1. Nothing enforces emptiness check!
2. Scala has a great type for these :-)
Stringly Typed の例2:
Option の代わりに空文字を使う
10. Collective Abuse
• Scala has a massive
collection library
• Loads of built-ins too
– Case classes
– Functions and partials
– Tuples, tuples, tuples
• Fairly easy to abuse
Scalaにはたくさんのコレクション、caseクラス、関数、
タプルがある。これらは、実は簡単に濫用できてしまう
11. Collective Abuse
• Common anti-patterns:
– Too many inline steps
– Tuple overload
val actors: List[(Int, String, Double)] = // ...
def bestActor(query: String) =
actors.filter(_._2 contains query)
.sortBy(-_._3)
.map(_._1)
.headOption
1. What does this even do?!
2. How does data flow here?
アンチパターン:
一行に処理を詰め込み過ぎる
12. Collective Abuse
• Common anti-patterns:
– Too many inline steps
– Tuple overload
val actors: List[(Int, String, Double)] = // ...
def bestActor(query: String) = {
val matching = actors.filter(_._2 contains query)
val bestByScore = matching.sortBy(-_._3).headOption
bestByScore.map(_._1)
} Name intermediate steps!
中間状態に名前を付けよう!
13. Collective Abuse
• Common anti-patterns:
– Too many inline steps
– Tuple overload
val actors: List[(Int, String, Double)] = // ...
def bestActor(query: String) =
actors.filter(_._2 contains query)
.sortBy(-_._3)
.map(_._1)
.headOption
What’s with all these
underscores?
アンチパターン:
タプルの使いすぎ
14. Collective Abuse
• Common anti-patterns:
– Too many inline steps
– Tuple overload
case class Actor(id: Int, name: String, score: Double)
def bestActor(query: String, actors: List[Actor]) =
actors.filter(_.name contains query)
.sortBy(-_.score)
.map(_.id)
.headOption
Scala classes are cheap.
Use them.
Scala ではcaseクラスを簡単に定義できる
どんどん使おう