Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and And...
Search
AGAWA Koji
December 12, 2019
Programming
840
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
79
49k
PipeCDプラグインへの期待 / Anticipating PipeCD Plugins
atty303
0
140
EmscriptenでC/C++アプリをWASM化してブラウザで動かしてみた
atty303
0
700
良いソフトウェアとコードレビュー / Good software and code review
atty303
38
19k
Scala + Caliban で作るGraphQL バックエンド / Making GraphQL Backend with Scala + Caliban
atty303
0
620
もう一つのビルドツール 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
1.1k
ADC2016: Axion meets HashiCorp
atty303
0
870
Other Decks in Programming
See All in Programming
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
180
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
R8 の設定見直しでアプリサイズ削減
andpad
0
200
Kubernetes祭り #2 OP/ED
bells17
0
110
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
580
golang-bob.pdf
iwatsukayura
0
120
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
470
Workers Cache を知る
syumai
0
350
AI主導のプログラミング学習アプリを作ってみた
econanringo
0
150
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
350
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.8k
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
370
Featured
See All Featured
Building Adaptive Systems
keathley
44
3.2k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
550
Amusing Abliteration
ianozsvald
1
330
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
840
Context Engineering - Making Every Token Count
addyosmani
9
1.2k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
400
Designing for Timeless Needs
cassininazir
1
520
The SEO identity crisis: Don't let AI make you average
varn
0
580
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
5
780
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1.1k
Large-scale JavaScript Application Architecture
addyosmani
516
110k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.8k
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