Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and And...
Search
AGAWA Koji
December 12, 2019
Programming
830
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and Android
社内カンファレンスで発表した資料です。
AGAWA Koji
December 12, 2019
More Decks by AGAWA Koji
See All by AGAWA Koji
Software Architecture in an AI-Driven World
atty303
80
48k
PipeCDプラグインへの期待 / Anticipating PipeCD Plugins
atty303
0
130
EmscriptenでC/C++アプリをWASM化してブラウザで動かしてみた
atty303
0
680
良いソフトウェアとコードレビュー / Good software and code review
atty303
38
18k
Scala + Caliban で作るGraphQL バックエンド / Making GraphQL Backend with Scala + Caliban
atty303
0
600
もう一つのビルドツール mill で作る Docker イメージ / Build docker image with mill the yet another build tool
atty303
2
2.6k
Case of Ad Delivery System is Implemented by Scala and DDD
atty303
4
3.7k
ログのメトリックを取ってみる話
atty303
0
1k
ADC2016: Axion meets HashiCorp
atty303
0
850
Other Decks in Programming
See All in Programming
SlackアプリとLambdaの 連携を構築した話
pawn_4_s
1
110
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
140
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
360
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
150
AWS DevOps AgentのAzure接続機能を検証して見えた活用法/Use Cases Verified for the AWS DevOps Agent's Azure Connectivity Feature
masakiokuda
1
230
源内ハンズオン概要編
hideg
0
170
React本体のコードリーディング
high_g_engineer
1
140
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
4
1.9k
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
550
Japan Community Day at Kubecon + CloudNativeCon Japan 2026: Learning Container Privilege Control by Building My Own Low-Level Container Runtime
ternbusty
1
150
その節約、円になってますか?
isamumumu
1
730
Laravelで学ぶ Webアプリケーションチューニング入門/web_application_tuning_101
hanhan1978
4
1.8k
Featured
See All Featured
Building Applications with DynamoDB
mza
96
7.2k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
520
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
370
Unsuck your backbone
ammeep
672
58k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
Prompt Engineering for Job Search
mfonobong
0
400
The Invisible Side of Design
smashingmag
301
52k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
430
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
The Cult of Friendly URLs
andyhume
79
7k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.3k
Believing is Seeing
oripsolob
1
190
Transcript
Scala.jsͰAndroidͱυϝΠ ϯΛڞ༗͠Α͏ Ѩ ߞ࢘ @atty303 AI tech studio / CyberAgent,
Inc. © 2019 CyberAgent, Inc. 1/35
Ѩ ߞ࢘ ςοΫϦʔυ @ൢଅαΠωʔδAd • ۀྺ 14 / Scala
ྺ 5 • ήʔϜ͖ (Steam ϝΠϯ) ࠷ۙࣨαΠΫϦϯάΛؤ ுͬͯ·͢ɻ © 2019 CyberAgent, Inc. 2/35
ൢଅαΠωʔδAd © 2019 CyberAgent, Inc. 3/35
© 2019 CyberAgent, Inc. 4/35
৽͍͠ސ٬ମݧͱ࣮ళ ฮͷՁΛ͢Δ Λϛογϣϯʹܝ͛ɺίϯϏχ ΤϯεετΞɾυϥοάετ Ξɾεʔύʔͱ͍࣮ͬͨళฮͷ σδλϧԽɾϝσΟΞԽΛɺσ δλϧαΠωʔδͱίϯςϯπ ʗࠂ৴γεςϜΛ༻͍࣮ͯ ݱ͠·͢ɻ ©
2019 CyberAgent, Inc. 5/35
αΠωʔδͷٕज़ελοΫ • Android 6.0 (API 23) • Kotlin • MediaPlayer
+ TextureView • WebView + Scala.js © 2019 CyberAgent, Inc. 6/35
ϨΠϠʔͱٕज़ • Kotlin υϥΠόͷΈ • υϝΠϯ͓ΑͼΞϓϦέʔ γϣϯ Scala.js © 2019
CyberAgent, Inc. 7/35
Kotlin/Scalaͷίʔυྔ Kotlin $ wc -l `find context/signage/ -name '*.kt'` 1815
total Scala $ wc -l `find context/signage/ -name '*.scala'` 6515 total © 2019 CyberAgent, Inc. 8/35
Scala.js © 2019 CyberAgent, Inc. 9/35
Scala.js • Scala ͔Β JavaScript ʹίϯύΠϧ͢Δͭ • 2013͔Β։ൃ͕ଓ͍͍ͯΔ • ࠷ۙ
1.0-RC ͕ग़ͯɺ20201݄ʹ 1.0 ϦϦʔε༧ ఆ © 2019 CyberAgent, Inc. 10/35
ݸਓతͳҹ • ScalaͰJSॻ͚Δͷ໘ന͍ • JSΤίγεςϜͱͷ૬ੑΛߟ͑ͨΒTypeScript͕ ແͰʁ • มԽͷܹ͍͠ React, Vue,
... ͳͲΛ͑Δͷ͔ • ͍ॴ͕ͦ͠͏ © 2019 CyberAgent, Inc. 11/35
ࠓճ࠾༻ͨ͠ཧ༝ • υϝΠϯΛαʔόʔͱαΠωʔδͰڞ༗ͨ͠ ͔ͬͨ • αʔόʔ Scala Ͱ֬ఆɺαΠωʔδ Android Ͱ֬ఆɺͱ͍͏ঢ়گ
• Android ΑΓ(׳Ε͍ͯΔ) Web ٕज़ʹد͔ͤͨͬ ͨ → Scala.js ͕ϕετϚονͰʂʁ © 2019 CyberAgent, Inc. 12/35
Scala.js ͷಋೖ • project/plugins.sbt ʹ Scala.js ϓϥάΠϯΛՃ addSbtPlugin("org.scala-js" % "sbt-scalajs"
% "0.6.31") // JVM/JS ΫϩεϏϧυ͕ඞཁͳ߹ԼهՃ addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.1") © 2019 CyberAgent, Inc. 13/35
Scala.js ͷಋೖ • ϓϩδΣΫτઃఆͰ enablePlugins(ScalaJSPlugin) ΛՃ͢Δ lazy val signageDriverSignageJs =
(project in file("context/signage/driver/signage-js")) .dependsOn(signageApplication.js) .enablePlugins(ScalaJSPlugin) .settings(...) © 2019 CyberAgent, Inc. 14/35
Scala.js ͷಋೖ • sbt Ͱ fastOptJS λεΫΛ࣮ߦ͢Δͱ js ͕ॻ͖ग़ ͞ΕΔ
$ sbt fastOptJS Ҏ্ɺ؆୯Ͱ͢Ͷ © 2019 CyberAgent, Inc. 15/35
Scala.jsಋೖ·Ͱ • औΓֻ͔͔ͬͯΒ 2 ఔͰྃ • ͱͱ Scala.js ͷ͜ͱΛҰߟྀ͍ͯ͠ͳ͍ ίʔυϕʔε
• υϝΠϯϥΠϒϥϦґଘ͕গͳ͔ͬͨ © 2019 CyberAgent, Inc. 16/35
AndroidͱScala.jsͷܨ͗ © 2019 CyberAgent, Inc. 17/35
interop KotlinͷυϥΠόͱScala.jsͷΞϓϦέʔγϣϯ Λܨ͙ύʔπ © 2019 CyberAgent, Inc. 18/35
Scala.js ଆ import scala.scalajs.js import scala.scalajs.js.annotation.JSGlobal @js.native @JSGlobal object interop
extends js.Object { def requestCacheContents(urls: js.Array[String]): Unit = js.native def publish( dataBase64: String, method: String, endpoint: String, headers: String ): Unit = js.native def loadSlot( index: Int, src: String, poster: String, isImage: Boolean, invertedSlotRotation: Int ): Unit = js.native def flipSlot(index: Int): Unit = js.native } © 2019 CyberAgent, Inc. 19/35
Androidଆ import android.webkit.JavascriptInterface import android.webkit.WebView class WebViewInterop( val requestCacheContentsDelegate: RequestCacheContentsDelegate
// publish ... ) { fun addToWebView(webView: WebView): WebView { webView.addJavascriptInterface(this, "interop") return webView } @JavascriptInterface fun requestCacheContents(urls: Array<String>): Unit { requestCacheContentsDelegate.apply(urls) } // publish ... } © 2019 CyberAgent, Inc. 20/35
υϝΠϯͷશମ૾ © 2019 CyberAgent, Inc. 21/35
SignageProgram ͜ΕΛαʔόʔͰΈཱͯɺAndroidଆͰར༻͢Δͷ ͕ओత • Protocol Buffers Ͱαʔόʔ͔ΒAndroidૹ৴ • Scala ͰγϦΞϥΠζͯ͠ɺScala.js
ͰσγϦΞϥ Πζ • ྆ଆͱ ScalaPB Ͱ؆୯ʹͰ͖ͨ © 2019 CyberAgent, Inc. 22/35
Scala.js ར༻ͷ © 2019 CyberAgent, Inc. 23/35
Long ܕͷར༻ʹؾΛ͚ͭΔ • JVM ͷ Long 63bit • JS
ͷ Number Double ૬ͳͷͰԾ෦ 52bit • LocalTime#toNanoOfDay() Ͱਫ਼མͪʹૺ۰ • ࣮ߦ࣌ΤϥʔʹͳΔ • toSecondOfDay() + getNano() ͰରԠ © 2019 CyberAgent, Inc. 24/35
Java API جૅతͳ෦ͷΈར༻Մ ೳ • ৄࡉ https://github.com/scala-js/scala-js/tree/ master/javalib/src/main/scala/java Λࢀর ©
2019 CyberAgent, Inc. 25/35
Java API Ͱࠓճࠔͬͨͱ͜Ζ • java.time ύοέʔδ͕σϑΥϧτͰ͑ͳ͍ • "com.zoepepper" %%% "scalajs-jsjoda-as-java-
time" ͷར༻Ͱղܾ © 2019 CyberAgent, Inc. 26/35
Scala.js ͱ͍ͬͯΔϥΠϒϥϦ • ScalaPB • "org.typelevel" %%% "cats-core" • "org.typelevel"
%%% "cats-effect" • "com.beachape" %%% "enumeratum" • "io.estatico" %%% "newtype" • "eu.timepit" %%% "refined" • "io.lemonlabs" %%% "scala-uri" © 2019 CyberAgent, Inc. 27/35
UseCase Λ JVM/JS Ͱڞ༗͢Δ • DisplayContentUseCase JVM/JS Ͱڞ༗͍ͨ͠ •
JS ͰαΠωʔδʹίϯςϯπΛදࣔ͢Δ • JVM Ͱදࣔ͞ΕΔίϯςϯπΛϓϨϏϡʔ͢ Δ • UseCase ͕ڞ௨ͳͷͰ LocationRepository ͳͲ ͷ Repository JVM ͱ JS ͰಉҰ trait Λ͍ͨ © 2019 CyberAgent, Inc. 28/35
࠷ॳͷܗ trait LocationRepository { def findById(id: LocationId): ConnectionIO[Option[Location]] } •
doobie.free.connection.ConnectionIO • doobie (JDBC ϥΠϒϥϦ) ͷ JS ൛ଘࡏ͠ͳ ͍(વ) • JS ͔Β ConnectionIO ܕΛར༻Ͱ͖ͳ͍ • ͳΜΒ͔ͷநԽ͕ඞཁ © 2019 CyberAgent, Inc. 29/35
RepositoryͷநԽͨ͠ܗ trait LocationRepository[F[_]] { def findById(id: LocationId): F[Option[Location]] } //
JVM trait PostgresLocationRepository extends LocationRepository[ConnectionIO] { def findById(id: LocationId): ConnectionIO[Option[Location]] } // JS trait InMemoryLocationRepository extends LocationRepository[IO] { def findById(id: LocationId): IO[Option[Location]] } © 2019 CyberAgent, Inc. 30/35
TransactorநԽ trait Transactor[M[_], N[_]] { def trans(implicit ev: Bracket[N, Throwable]):
M ~> N } // Doobie class DoobieTransactor[N[_]]( underlying: doobie.util.transactor.Transactor[N] ) extends Transactor[ConnectionIO, N] { override def trans(implicit ev: Bracket[N, Throwable]): ConnectionIO ~> N = underlying.trans } // JS class RawTransactor[N[_]]( implicit liftIO: LiftIO[N] ) extends Transactor[IO, N] { override def trans(implicit ev: Bracket[N, Throwable]): IO ~> N = LiftIO.liftK[N] } © 2019 CyberAgent, Inc. 31/35
རศੑͷͨΊʹ Transactable ܕΫϥ εΛఆٛ trait Transactable[M[_]] { def transact[A](transactor: Transactor[M,
IO])(m: M[A])(implicit ev: Bracket[IO, Throwable]): IO[A] } // Doobie implicit lazy val transactableForConnectionIO: Transactable[ConnectionIO] = new Transactable[ConnectionIO] { override def transact[A]( transactor: repository.Transactor[ConnectionIO, IO] )(m: ConnectionIO[A])(implicit ev: Bracket[IO, Throwable]): IO[A] = transactor.trans.apply(m) } // JS implicit lazy val transactable: Transactable[IO] = new Transactable[IO] { override def transact[A](transactor: Transactor[IO, IO])(m: IO[A])(implicit ev: Bracket[IO, Throwable]): IO[A] = transactor.trans.apply(m) } © 2019 CyberAgent, Inc. 32/35
UseCase͔Βར༻͢Δ class DisplayContentUseCase[F[_]]( transactor: Resource[IO, Transactor[F, IO]], locationRepository: LocationRepository[F] )(
implicit bracketForF: Bracket[F, Throwable], transactableForF: Transactable[F] ) { def apply(locationId: LocationId): IO[Unit] = { val transIo: IO[Location] = transactor.use { xa => val io = for { maybeLocation <- locationRepository.findById(locationId) location <- maybeLocation.liftTo[F](new LocationNotFountError) } yield location Transactable[F].transact(xa)(io) } } for { location <- transIo // ... } yield () } © 2019 CyberAgent, Inc. 33/35
UseCaseͷڞ༗ʹ͍ͭͯ • UseCaseͷڞ༗ͪΐͬͱΓ͔͗͢͠Εͳ͍ • F[_] ͕͍ͬͺ͍ͰΑ͘Θ͔Βͳ͍ʁ • ࣮ࡍɺJVM/JS ͰͦΕͧΕͰUseCaseඍົ ʹҧ͏
• ແཧʹڞ௨ԽͤͣɺJVM/JS ͰUseCaseΛ͚Δ͔ © 2019 CyberAgent, Inc. 34/35
Scala.js ಋೖͷॴײ • Scala.js ࣗମશʹ 1.0 ΫΦϦςΟ͋Δ • पลϥΠϒϥϦॆ࣮ (Pure
Scala ͳϥΠϒϥϦ ΄΅͑Δ) • ࠓճͷΑ͏ʹ༻్͕ϋϚΕ͘͢͝༗༻ • ͰWebϑϩϯτΤϯυΛScala.jsͰॻ͜͏ͱ· ͩࢥΘͳ͍ © 2019 CyberAgent, Inc. 35/35