Upgrade to Pro — share decks privately, control downloads, hide ads and more …

明日から業務で使うScala

 明日から業務で使うScala

Avatar for Ikuo Suyama

Ikuo Suyama

March 21, 2017
Tweet

More Decks by Ikuo Suyama

Other Decks in Programming

Transcript

  1. ͓࿩͢Δ͜ͱ ˕  νϡʔτϦΞϧͰڭ ͑ͯ͘Εͳ͍ʢग़ͯ ͖ͯ΋αϥοͱ…ʣ ͚Ͳݱ࣮ੈքͰ໾ཱ ͭScala ˕  ۀ຿Ͱ࢖͍࢝Ίͨࠒ ʹࠔͬͨ΋ͷɾૣ͘

    ஌͓͖͔ͬͯͨͬͨ Scala ◦ ʮ͋ͷ࣌ΞϨΛ஌͍ͬͯ Ε͹…ʯ ຊ೔ͷςʔϚ ۀ຿Ͱ࢖͏Scala ͓࿩͠ͳ͍͜ͱ ˕  ͳͥScalaΛ࢖͏͔ɺ ਓྨ͸ScalaͰ޾ͤ ʹͳΕΔ͔ ˕  object, traitͳͲجૅ తͳจ๏ͷ࿩ ˕  M͔Β͸͡·Δ΍ͭ ͱؔ਺ܕͷ࿩
  2. IDE

  3. υఆ൪Emacs࢖͍ͷํΰϝϯφαΠ… Scala։ൃͰศརͳػೳ IDE: IntelliJ + Scala Plugin ˕  γϣʔτΧοτ ◦ 

    ^ + Shift + P : ܕ৘ใ ◦  ⌘ + B/⌘ + ^ + B : ఆٛɺ࣮૷Jump ◦  Alt + Enter : ͳΜͰ΋मਖ਼ ◦  Shift x2: ͳΜͰ΋ݕࡧ , etc… “ ๩͍͠ਓͷͨΊͷIntelliJ IDEAγϣʔτΧοτूʢ´-`ʣ ˕  ScalaWorkSheet ◦  New -> Scala Worksheet ◦  IDEΤσΟλػೳ͕࢖͑ΔREPL ◦  ͪΐͬͱෳࡶͳίʔυͷݕূʹ࢖͏
  4. ఆ൪Ϗϧυπʔϧ ͪΐͬͱ΍΍͍͜͠… SBT organization := "io.martin.lover” version := "0.1.0” scalaVersion

    := "2.12.1” libraryDependencies ++= Seq ( "org.specs2" %% "specs2-core" % "3.8.9" % "test” ) !? !? !?
  5. ͜Ε͚֮ͩ͑Δ ˕  build.sbt͸Scalaͱͯ͠ίϯύΠϧ͞ΕΔ ◦  Scalaίʔυ͕ॻ͚Δɻval, lazy val, def ͕࡞ΕΔ ‒ object,

    class͸τοϓϨϕϧʹ͸࡞Εͳ͍ ◦  ݟ֮͑ͷͳ͍ه߸͸⌘+B౳ͰఆٛΛ୳ΕΔ ˕  build.sbt DSL ◦  `:=` … ࠨล(setting/task key)ʹӈลͷ݁ՌΛઃఆ͢Δ ◦  `+=` … ࠨลʹӈลͷ݁ՌΛ௥Ճ͢Δ ◦  `++=` … ࠨลʹӈลͷ݁ՌΛෳ਺Ұ౓ʹ௥Ճ͢Δ SBT
  6. SBT: Setting/Task “SBT-Ϗϧυఆٛ ˕  ࠨลɿΩʔ ◦  SettingKey[T] αϒϓϩδΣΫτಡΈࠐΈ࣌ɺҰ౓͚ͩܭࢉ ◦  TaskKey[T]

    ຖճܭࢉɻ෭࡞༻Λ൐͏Մೳੑ͋Γ ˕  ΦϖϨʔλʔɿϚΫϩ ˕  ӈลɿຊจ ◦  ઃఆ͢Δ஋
  7. 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ビルドがあるときは`%%`
  8. SBT: Multi Projects “SBT−ϚϧνϓϩδΣΫτ lazy val `core` = (project in

    file("core")) lazy val `web` = project ◦  ڞ௨Ϟσϧ΍Util ͷ੾Γग़͠౳ɺෳ਺ͷҟଘͷ͋Δϓϩδ ΣΫτΛ࡞Γ͍ͨͱ͖ʢ΄ͱΜͲຖճ…ʣ ◦  project ͸ϚΫϩͰɺม਺໊ΛIDʹͨ͠Projectܕͷ஋Λ࡞੒ ͢Δ ◦  IDͱσΟϨΫτϦ͕ಉ࣌͡͸লུͰ͖Δ
  9. 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)で 同じタスクを先に実行 &クラスパスに読み込み
  10. লུ val list = Seq(1, 2, 3, 4, 5) list

    map { i => i + 1 } わかる !!? ˕  ஌ͬͯͳ͍ͱಡΊͳ͍ܗ͕ଟ͍ɺɺ ˕  ༰ࣻͳ͘ϥΠϒϥϦͷνϡʔτϦΞϧ౳Ͱग़ ͯɺDSLͱݟ෼͚͔ͭͳͯ͘ࠔͬͨ ˕  සग़ͷܗΛPickupͯ͠঺հ͠·͢ɻ ◦  ͋ͱ͸ग़͖ͯͨͱ͖ʹؤுΔ “Scala ͷলུϧʔϧૣ֮͑ “ScalaͰͷϝιουݺͼग़͠ͷॻ͖ํҰཡͱਪ঑͞ΕΔॻ͖ํ
  11. লུ: ϥϜμࣜ 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ܕͷม਺ͱͯ͠ॻ͚Δɻӈล͸ ϥϜμࣜͱݺ͹ΕΔ
  12. লུ: தஔه๏ 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} ◦  . ͱ () ͷলུʢதஔه๏ʣ ◦  ؔ਺ΛҾ਺ʹ̍ͭऔΔ৔߹ɺ() ͷ୅ΘΓʹ {} Λ࢖ͬͯྑ͍ この形が頻出! ◦  ͢΂ͯͷҾ਺͕̍ճ͔͠࢖ΘΕͳ͍৔߹ɺҾ਺ఆٛΛলུ ͯ͠ `_` ʹͰ͖Δ(!) _ は一つ目の引数、の意。頻出!!
  13. ϥΠϒϥϦʹݟΔதஔه๏ 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
  14. 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ࣜͷ࠶આ
  15. 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 ʹల։͞ΕΔ なるほど わからん
  16. 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͕ฦΔ
  17. 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จͰॻ͖׵͑ΔϓϧϦΫνϟϯεʂ どこで終わるの、、、 ()多すぎ、、
  18. 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の検証のみで値を 利用しないときは _ で受ける “ଞͷܕͰͷαϯϓϧ
  19. 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 この名前のファイルは デフォルトで読み込まれる
  20. 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でクラスパス外ファイルも指定可能
  21. ύοέʔδ: 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 ΋࡞ΕΔ
  22. 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