Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

明日から業務で使うScala

1,745 views

Published on

アドテクスタジオ×Scala勉強会
https://adtechstudio.connpass.com/event/50607/
の発表資料です。

Published in: Software
  • Be the first to comment

明日から業務で使うScala

  1. 1. 明日から 業務で使う Scala 陶山育男 Cyberagent アドテクスタジオ 2017/03/21
  2. 2. 陶山 育男 すやま いくお Cyberagent アドテクスタジオ LODEO サーバーサイドエンジニア Scala歴 1年ちょっと
  3. 3. ◎スマートフォン動画広告ネッ トワーク ◎国内最大級のタテ型動画広告 在庫 ◎配信サーバーなど、バックエ ンドにScalaを採用 “タテフル”
  4. 4. 質問 お客様の中に 業務でScalaを使わ れている方 はいらっしゃいますか?
  5. 5. お話すること ◎ チュートリアルで教 えてくれない(出て きてもサラッと…) けど現実世界で役立 つScala ◎ 業務で使い始めた頃 に困ったもの・早く 知っておきたかった Scala ○「あの時アレを知ってい れば…」 本日のテーマ 業務で使うScala お話しないこと ◎ なぜScalaを使うか、 人類はScalaで幸せ になれるか ◎ object, traitなど基礎 的な文法の話 ◎ Mからはじまるやつ と関数型の話
  6. 6. This is For Beginners+ チュートリアル終わって、いざ実務! な方、または使い始めて日が浅い方 チュートリアル未済の方のための良チュートリアル “ドワンゴさんの研修資料 “はてなさんの資料 “Fringe81さんの資料 弊社のScalaブログ ※チュートリアルではありませんが役立つ情報満載!
  7. 7. 1. 業務で使う 開発環境
  8. 8. IDE
  9. 9. ド定番Emacs使いの方ゴメンナサイ… Scala開発で便利な機能 IDE: IntelliJ + Scala Plugin ◎ ショートカット ○ ^ + Shift + P : 型情報 ○ ⌘ + B/⌘ + ^ + B : 定義、実装Jump ○ Alt + Enter : なんでも修正 ○ Shift x2: なんでも検索 , etc… “ 忙しい人のためのIntelliJ IDEAショートカット集(´-`) ◎ ScalaWorkSheet ○ New -> Scala Worksheet ○ IDEエディタ機能が使えるREPL ○ ちょっと複雑なコードの検証に使う
  10. 10. ビルドツール
  11. 11. 定番ビルドツール ちょっとややこしい… SBT organization := "io.martin.lover” version := "0.1.0” scalaVersion := "2.12.1” libraryDependencies ++= Seq ( "org.specs2" %% "specs2-core" % "3.8.9" % "test” ) !? !? !?
  12. 12. これだけ覚える ◎ build.sbtはScalaとしてコンパイルされる ○ Scalaコードが書ける。val, lazy val, def が作れる ◉object, classはトップレベルには作れない ○ 見覚えのない記号は⌘+B等で定義を探れる ◎ build.sbt DSL ○ `:=` … 左辺(setting/task key)に右辺の結果を設定する ○ `+=` … 左辺に右辺の結果を追加する ○ `++=` … 左辺に右辺の結果を複数一度に追加する SBT
  13. 13. SBT: Setting/Task “SBT-ビルド定義 ◎ 左辺:キー ○ SettingKey[T] サブプロジェクト読み込み時、一度だけ計算 ○ TaskKey[T] 毎回計算。副作用を伴う可能性あり ◎ オペレーター:マクロ ◎ 右辺:本文 ○ 設定する値
  14. 14. SBT: Libraly Dependencies “SBT-ライブラリ依存性 libraryDependencies += ”groupID” %% ”artifactID” % ”revision" % ”configuratioin" ○ 依存管理はApache Ivy 、Mavenと同じ感覚でつかえる ○ デフォルトでMavenのpublicRepを探す libraryDependencies ++= Seq ( "commons-daemon" % "commons-daemon" % "1.0.15", "org.specs2" %% "specs2-core" % "3.8.9" % "test” excludeAll ExclusionRule(organization = "log4j") ) 特定のライブラリを弾きたいことが 稀によくある ○ %% はscalaVersionキーで定義したScalaのバージョン(ク ロスビルド)のバイナリを探す ○ scalaVersion := “2.12.1“とした場合は “_2.12“ をartifactIDに 補うだけ。↑の例では “secs2-core_2.12“ を探す javaライブラリは`%` Scalaビルドがあるときは`%%`
  15. 15. SBT: Multi Projects “SBT−マルチプロジェクト lazy val `core` = (project in file("core")) lazy val `web` = project ○ 共通モデルやUtil の切り出し等、複数の異存のあるプロジ ェクトを作りたいとき(ほとんど毎回…) ○ project はマクロで、変数名をIDにしたProject型の値を作成 する ○ IDとディレクトリが同じ時は省略できる
  16. 16. SBT: Multi Projects Sample “サンプル – フル版 lazy val commonSettings = Seq( organization := "io.martin.lover", version := "0.1.0", scalaVersion := "2.12.1" ) lazy val `root` = (project in file(".")) .aggregate(`core`, `web`) lazy val `core` = (project in file("core")) .settings(commonSettings) lazy val `web` = (project in file("web")) .settings(commonSettings) .settings(libraryDependencies ++= webLibraryDependencies) .dependsOn(core) lazy val webLibraryDependencies = Seq(/* ... */) $ sbt sbt> project core サブプロジェクト切り替え。 初回時自動で必要なデレィクトリが 作成される 集約。 集約される側(core, web)で 同じタスクを実行。 依存。 依存される側(core)で 同じタスクを先に実行 &クラスパスに読み込み
  17. 17. 2. 業務で使う 頻出イディオム
  18. 18. 省略
  19. 19. 省略 val list = Seq(1, 2, 3, 4, 5) list map { i => i + 1 } わかる !!? ◎ 知ってないと読めない形が多い、、 ◎ 容赦なくライブラリのチュートリアル等で出 て、DSLと見分けつかなくて困った ◎ 頻出の形をPickupして紹介します。 ○ あとは出てきたときに頑張る “Scala の省略ルール早覚え “Scalaでのメソッド呼び出しの書き方一覧と推奨される書き方
  20. 20. 省略: ラムダ式 val list = Seq(1, 2, 3, 4, 5) def double(i: Int): Int = { i * 2 } list.map(double) val double:(Int) => Int = (i: Int) => i * 2 list.map(double) ○ doubleは普通の関数定義。mapは引数1つの関数を引数に 取る高階関数。 関数型定義 のS.S. Function1のインスタンス =無名関数=ラムダ式 ○ 関数定義の省略。Function1型の変数として書ける。右辺 はラムダ式と呼ばれる
  21. 21. 省略: 中置記法 val list = Seq(1, 2, 3, 4, 5) val double2:(Int) => Int = (i: Int) => i * 2 list.map(i => i * 2) list map { i => i * 2 } ○ 引数の型が自明の場合、引数型を省略してよい ○ 引数が1つの場合、()を省略して良い : Int と () を省略 list map {_ * 2} ○ . と () の省略(中置記法) ○ 関数を引数に1つ取る場合、() の代わりに {} を使って良い この形が頻出! ○ すべての引数が1回しか使われない場合、引数定義を省略 して `_` にできる(!) _ は一つ目の引数、の意。頻出!!
  22. 22. ライブラリに見る中置記法 val name = "martin.lover" val memberId: Option[Long] = DB readOnly { implicit session => sql"select id from members where name = ${name}" .map(rs => rs.long("id")) .single .apply() } scalikeJDBC "Specs2" should { "use infix notation" in { "test" mustEqual "test" } } specs2 val route: Route = get { pathPrefix("item" / LongNumber) { id => // there might be no item for a given id val maybeItem: Future[Option[Item]] = fetchItem(id) akka-http should, inが高階関数 () => Flagment readOnly が高階関数 DBSession => A pathPrefixが高階関数 Directive1
  23. 23. For Comprehension
  24. 24. For Comprehension val maybeName: Option[String] = Some("martin.lover") val maybeAge: Option[Int] = Some(33) case class Person(name:String, age:Int) val person: Option[Person] = for { name <- maybeName age <- maybeAge } yield Person(name, age) わかる !!? ◎ 覚えるとすごい便利&強力 ◎ 制御を追わなくて良くなるので、可読性↑↑ ○ 好みはあると思います。。LODEOでは頻出。 わかる “ Scalaスケーラブルプログラミング(コップ本) 23章 for式の再説
  25. 25. For Comprehension: 展開 val maybeName: Option[String] = Some("martin.lover") val maybeAge: Option[Int] = Some(33) val person: Option[Person] = for { name <- maybeName age <- maybeAge } yield Person(name, age) val person: Option[Person] = maybeName flatMap { name => maybeAge map { age => Person(name, age) } } ○ `<-` はジェネレーターと呼ばれる ○ flatMap とか map とか foreach とか withFilter に展開される なるほど わからん
  26. 26. For Comprehension: 覚え方 val person: Option[Person] = for { name <- maybeName age <- maybeAge addr <- maybeAddr } yield Person(name, age) Forを始めた外側の 型が最後まで続く Option[String]外側剥がれる name:String 外側の型を 揃える Option[T] 中はなんでも良い 計算失敗したら それ以上実行 しない 最後まで成功したら 外側の型でくるんで返す ○ flatMap, mapが実装されていれば、ここでいう「外側の型」 として使える=「文脈付きの型」 ○ 上の例なら、maybeName,maybeAge,maybeAddrがすべて Someなら Some(Person), どれかNoneならNoneが返る
  27. 27. For Comp~: うれしいこと maybeName match { case Some(name) => maybeAge match { case Some(age) => maybeAddr match { case Some(addr) => Person(name, age) case None => None } case None => None } case None => None } (maybeName, maybeAge, maybeAddr) match { case (Some(name), Some(age), Some(addr)) => Some(Person(name, age)) case _ => None } ○ こういうコードが見やすく改善できる ○ matchのネストを見たらfor文で書き換えるプルリクチャンス! どこで終わるの、、、 ()多すぎ、、
  28. 28. For Comp~: flatMap御三家 ◎Option[T] ○ 値があるかもしれないしないかもしれない ◎Future[T] ○ 値が未来に取得できるかもしれない ◎Either[T] ○ 右かもしれないし左かもしれない
  29. 29. For Comp~: Either flatMap def validateName(name: String): Either[String, String] def validateAge(age: Int): Either[String, Int] val validPerson = for { name <- maybeName toRight "Name is Required" age <- maybeAge toRight "Age is Required" _ <- validateName(name) _ <- validateAge(age) } yield Person(name, age) ○ Eitherの例。複数重なるとmatchネストしがち ○ Scala 2.12 で Either が Right-Biasになった!! ◉.rightを書かなくて良くなった Eitherを返したいので、 OptionをEitherに変換して Eitherでforを始める Eitherの検証のみで値を 利用しないときは _ で受ける “他の型でのサンプル
  30. 30. 3. 業務で使う デプロイ・運用
  31. 31. Config
  32. 32. Config libraryDependencies ++= Seq ( "com.typesafe" % "config" % "1.3.1" ) server { host = "localhost" port = 8080 } import com.typesafe.config.ConfigFactory val config = ConfigFactory.load val host = config.getString("server.host") val port = config.getString("server.port") println(s"server start: http://${host}:${port}”) ○ 基本 TypesafeConfig で困らない。多くのライブラリで利用 ○ Developmet / Staging / Producion で値を切り替えたい build.sbt resources/application.conf Hoge.scala この名前のファイルは デフォルトで読み込まれる
  33. 33. Config切り替え include "application.conf" server { host = ”unit.test.io" } ○ UT用は test/resources/に置くだけ。設定したものだけ上書き ○ 環境切り替えには各環境毎にそれぞれファイルを準備し、JVM Optionで渡すのが(おそらく)最も簡単 test/resources/application.conf include "application.conf" server { host = "martin.lover.io" } resources/production.conf $ java –Dconfig.resource=production.conf -jar martin_lover_io.jar $ java –Dconfig.file=/path/to/your/resources/production.conf application.confをincludeし、 producion.confで記載したものだけ上書きする 起動時にJVMオプションで読み込むファイルを指定 config.fileでクラスパス外ファイルも指定可能
  34. 34. パッケージ
  35. 35. パッケージ: sbt-native-packager lazy val `web` = (project in file("web")) : .enablePlugins(JavaServerAppPackaging) build.sbt “sbt-native-packager addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.4") project/plugins.sbt ○ sbt-assemblyをよく利用しているが、こちらはより多機能 ○ 起動スクリプトが作れる。DockerImage や (使わないと思う が)msi, dmg も作れる
  36. 36. “sbt-native-packager `Archetypes` サーバーなのかアプリ なのかなど、アプリ自 体の特性 `Formats` Universal Jarを作って固める `Formats` プラットフォーム特有 のファイルを追加する パッケージ: sbt-native-packager
  37. 37. lazy val `web` = (project in file("web")) : .settings(buildSettings) .enablePlugins(JavaServerAppPackaging) .enablePlugins(SystemVPlugin) .enablePlugins(RpmPlugin) build.sbt “build.sbtの完全なサンプル `Archetypes` サーバー起動 `Systemloaders` `Formats` ○ Systemloadersを指定 ◉Cent系で /etc/init.d のスクリプトがほしいときはSystemV ○ Rpmビルドを作成することを宣言 ◉Universalビルドではプラットフォーム依存のファイルは作成されない $ sbt sbt> project web [web] > rpm:packageBin パッケージ: sbt-native-packager
  38. 38. デプロイ
  39. 39. デプロイ ○ Lodeoの例。 ○ 特別なことはなく、JenkinsからJarを配って起動している デプロイ ビルド CI PR マージ
  40. 40. まとめ Scalaむずい ハイコンテクスト文化… でも楽しい!! よいScalaライフを!! 全く網羅的ではありませんでしたが 何か一つでも今後のお役に立てば…
  41. 41. Thanks! …And any questions?

×