Slide 1

Slide 1 text

໌೔͔Β ۀ຿Ͱ࢖͏ Scala ಃࢁҭஉ Cyberagent ΞυςΫελδΦ 2017/03/21

Slide 2

Slide 2 text

ಃࢁ ҭஉ ͢΍· ͍͓͘ Cyberagent ΞυςΫελδΦ LODEO αʔόʔαΠυΤϯδχΞ Scalaྺ 1೥ͪΐͬͱ

Slide 3

Slide 3 text

˕ εϚʔτϑΥϯಈը޿ࠂωο τϫʔΫ ˕ ࠃ಺࠷େڃͷλςܕಈը޿ࠂ ࡏݿ ˕ ഑৴αʔόʔͳͲɺόοΫΤ ϯυʹScalaΛ࠾༻ “λςϑϧ”

Slide 4

Slide 4 text

࣭໰ ͓٬༷ͷதʹ ۀ຿ͰScalaΛ࢖Θ Ε͍ͯΔํ ͸͍Βͬ͠Ό͍·͔͢ʁ

Slide 5

Slide 5 text

͓࿩͢Δ͜ͱ ˕  νϡʔτϦΞϧͰڭ ͑ͯ͘Εͳ͍ʢग़ͯ ͖ͯ΋αϥοͱ…ʣ ͚Ͳݱ࣮ੈքͰ໾ཱ ͭScala ˕  ۀ຿Ͱ࢖͍࢝Ίͨࠒ ʹࠔͬͨ΋ͷɾૣ͘ ஌͓͖͔ͬͯͨͬͨ Scala ○ ʮ͋ͷ࣌ΞϨΛ஌͍ͬͯ Ε͹…ʯ ຊ೔ͷςʔϚ ۀ຿Ͱ࢖͏Scala ͓࿩͠ͳ͍͜ͱ ˕  ͳͥScalaΛ࢖͏͔ɺ ਓྨ͸ScalaͰ޾ͤ ʹͳΕΔ͔ ˕  object, traitͳͲجૅ తͳจ๏ͷ࿩ ˕  M͔Β͸͡·Δ΍ͭ ͱؔ਺ܕͷ࿩

Slide 6

Slide 6 text

This is For Beginners+ νϡʔτϦΞϧऴΘͬͯɺ͍࣮͟຿ʂ ͳํɺ·ͨ͸࢖͍࢝Ίͯ೔͕ઙ͍ํ νϡʔτϦΞϧະࡁͷํͷͨΊͷྑνϡʔτϦΞϧ “υϫϯΰ͞Μͷݚमࢿྉ “͸ͯͳ͞Μͷࢿྉ “Fringe81͞Μͷࢿྉ ฐࣾͷScalaϒϩά ɹɹ※νϡʔτϦΞϧͰ͸͋Γ·ͤΜ͕໾ཱͭ৘ใຬࡌʂ

Slide 7

Slide 7 text

1. ۀ຿Ͱ࢖͏ ։ൃ؀ڥ

Slide 8

Slide 8 text

IDE

Slide 9

Slide 9 text

υఆ൪Emacs࢖͍ͷํΰϝϯφαΠ… Scala։ൃͰศརͳػೳ IDE: IntelliJ + Scala Plugin ˕  γϣʔτΧοτ ○  ^ + Shift + P : ܕ৘ใ ○  ⌘ + B/⌘ + ^ + B : ఆٛɺ࣮૷Jump ○  Alt + Enter : ͳΜͰ΋मਖ਼ ○  Shift x2: ͳΜͰ΋ݕࡧ , etc… “ ๩͍͠ਓͷͨΊͷIntelliJ IDEAγϣʔτΧοτूʢ´-`ʣ ˕  ScalaWorkSheet ○  New -> Scala Worksheet ○  IDEΤσΟλػೳ͕࢖͑ΔREPL ○  ͪΐͬͱෳࡶͳίʔυͷݕূʹ࢖͏

Slide 10

Slide 10 text

Ϗϧυπʔϧ

Slide 11

Slide 11 text

ఆ൪Ϗϧυπʔϧ ͪΐͬͱ΍΍͍͜͠… SBT organization := "io.martin.lover” version := "0.1.0” scalaVersion := "2.12.1” libraryDependencies ++= Seq ( "org.specs2" %% "specs2-core" % "3.8.9" % "test” ) !? !? !?

Slide 12

Slide 12 text

͜Ε͚֮ͩ͑Δ ˕  build.sbt͸Scalaͱͯ͠ίϯύΠϧ͞ΕΔ ○  Scalaίʔυ͕ॻ͚Δɻval, lazy val, def ͕࡞ΕΔ ‒ object, class͸τοϓϨϕϧʹ͸࡞Εͳ͍ ○  ݟ֮͑ͷͳ͍ه߸͸⌘+B౳ͰఆٛΛ୳ΕΔ ˕  build.sbt DSL ○  `:=` … ࠨล(setting/task key)ʹӈลͷ݁ՌΛઃఆ͢Δ ○  `+=` … ࠨลʹӈลͷ݁ՌΛ௥Ճ͢Δ ○  `++=` … ࠨลʹӈลͷ݁ՌΛෳ਺Ұ౓ʹ௥Ճ͢Δ SBT

Slide 13

Slide 13 text

SBT: Setting/Task “SBT-Ϗϧυఆٛ ˕  ࠨลɿΩʔ ○  SettingKey[T] αϒϓϩδΣΫτಡΈࠐΈ࣌ɺҰ౓͚ͩܭࢉ ○  TaskKey[T] ຖճܭࢉɻ෭࡞༻Λ൐͏Մೳੑ͋Γ ˕  ΦϖϨʔλʔɿϚΫϩ ˕  ӈลɿຊจ ○  ઃఆ͢Δ஋

Slide 14

Slide 14 text

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ビルドがあるときは`%%`

Slide 15

Slide 15 text

SBT: Multi Projects “SBT−ϚϧνϓϩδΣΫτ lazy val `core` = (project in file("core")) lazy val `web` = project ○  ڞ௨Ϟσϧ΍Util ͷ੾Γग़͠౳ɺෳ਺ͷҟଘͷ͋Δϓϩδ ΣΫτΛ࡞Γ͍ͨͱ͖ʢ΄ͱΜͲຖճ…ʣ ○  project ͸ϚΫϩͰɺม਺໊ΛIDʹͨ͠Projectܕͷ஋Λ࡞੒ ͢Δ ○  IDͱσΟϨΫτϦ͕ಉ࣌͡͸লུͰ͖Δ

Slide 16

Slide 16 text

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)で 同じタスクを先に実行 &クラスパスに読み込み

Slide 17

Slide 17 text

̎. ۀ຿Ͱ࢖͏ සग़ΠσΟΦϜ

Slide 18

Slide 18 text

লུ

Slide 19

Slide 19 text

লུ val list = Seq(1, 2, 3, 4, 5) list map { i => i + 1 } わかる !!? ˕  ஌ͬͯͳ͍ͱಡΊͳ͍ܗ͕ଟ͍ɺɺ ˕  ༰ࣻͳ͘ϥΠϒϥϦͷνϡʔτϦΞϧ౳Ͱग़ ͯɺDSLͱݟ෼͚͔ͭͳͯ͘ࠔͬͨ ˕  සग़ͷܗΛPickupͯ͠঺հ͠·͢ɻ ○  ͋ͱ͸ग़͖ͯͨͱ͖ʹؤுΔ “Scala ͷলུϧʔϧૣ֮͑ “ScalaͰͷϝιουݺͼग़͠ͷॻ͖ํҰཡͱਪ঑͞ΕΔॻ͖ํ

Slide 20

Slide 20 text

লུ: ϥϜμࣜ 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͸Ҿ਺̍ͭͷؔ਺ΛҾ਺ʹ औΔߴ֊ؔ਺ɻ 関数型定義 のS.S. Function1のインスタンス =無名関数=ラムダ式 ○  ؔ਺ఆٛͷলུɻFunction1ܕͷม਺ͱͯ͠ॻ͚Δɻӈล͸ ϥϜμࣜͱݺ͹ΕΔ

Slide 21

Slide 21 text

লུ: தஔه๏ 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 } ○  Ҿ਺ͷܕ͕ࣗ໌ͷ৔߹ɺҾ਺ܕΛলུͯ͠Α͍ ○  Ҿ਺͕̍ͭͷ৔߹ɺ()Λলུͯ͠ྑ͍ : Int と () を省略 list map {_ * 2} ○  . ͱ () ͷলུʢதஔه๏ʣ ○  ؔ਺ΛҾ਺ʹ̍ͭऔΔ৔߹ɺ() ͷ୅ΘΓʹ {} Λ࢖ͬͯྑ͍ この形が頻出! ○  ͢΂ͯͷҾ਺͕̍ճ͔͠࢖ΘΕͳ͍৔߹ɺҾ਺ఆٛΛলུ ͯ͠ `_` ʹͰ͖Δ(!) _ は一つ目の引数、の意。頻出!!

Slide 22

Slide 22 text

ϥΠϒϥϦʹݟΔதஔه๏ 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

Slide 23

Slide 23 text

For Comprehension

Slide 24

Slide 24 text

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ࣜͷ࠶આ

Slide 25

Slide 25 text

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 ʹల։͞ΕΔ なるほど わからん

Slide 26

Slide 26 text

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͕ฦΔ

Slide 27

Slide 27 text

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จͰॻ͖׵͑ΔϓϧϦΫνϟϯεʂ どこで終わるの、、、 ()多すぎ、、

Slide 28

Slide 28 text

For Comp~: flatMapޚࡾՈ ˕ Option[T] ○  ஋͕͋Δ͔΋͠Εͳ͍͠ͳ͍͔΋͠Εͳ͍ ˕ Future[T] ○  ஋͕ະདྷʹऔಘͰ͖Δ͔΋͠Εͳ͍ ˕ Either[T] ○  ӈ͔΋͠Εͳ͍͠ࠨ͔΋͠Εͳ͍

Slide 29

Slide 29 text

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の検証のみで値を 利用しないときは _ で受ける “ଞͷܕͰͷαϯϓϧ

Slide 30

Slide 30 text

̏. ۀ຿Ͱ࢖͏ σϓϩΠɾӡ༻

Slide 31

Slide 31 text

Config

Slide 32

Slide 32 text

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 この名前のファイルは デフォルトで読み込まれる

Slide 33

Slide 33 text

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でクラスパス外ファイルも指定可能

Slide 34

Slide 34 text

ύοέʔδ

Slide 35

Slide 35 text

ύοέʔδ: 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 ΋࡞ΕΔ

Slide 36

Slide 36 text

“sbt-native-packager `Archetypes` サーバーなのかアプリ なのかなど、アプリ自 体の特性 `Formats` Universal Jarを作って固める `Formats` プラットフォーム特有 のファイルを追加する ύοέʔδ: sbt-native-packager

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

σϓϩΠ

Slide 39

Slide 39 text

σϓϩΠ ○  Lodeoͷྫɻ ○  ಛผͳ͜ͱ͸ͳ͘ɺJenkins͔ΒJarΛ഑ͬͯىಈ͍ͯ͠Δ σϓϩΠ Ϗϧυ CI PR Ϛʔδ

Slide 40

Slide 40 text

·ͱΊ ScalaΉ͍ͣ ϋΠίϯςΫετจԽ… Ͱ΋ָ͍͠ʂʂ Α͍ScalaϥΠϑΛʂʂ શ͘໢ཏతͰ͸͋Γ·ͤΜͰ͕ͨ͠ Կ͔ҰͭͰ΋ࠓޙͷ͓໾ʹཱͯ͹…

Slide 41

Slide 41 text

Thanks! …And any questions?