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

KotlinConf 2018 ワークショップに参加してきた

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Yuki Anzai Yuki Anzai
October 19, 2018

KotlinConf 2018 ワークショップに参加してきた

KotlinConf 2018 報告会
https://kotlin.connpass.com/event/103395/

Avatar for Yuki Anzai

Yuki Anzai

October 19, 2018

More Decks by Yuki Anzai

Other Decks in Technology

Transcript

  1. Workshop day • 10݄3೔ʢKotlinConf day1ʣ • ΧϯϑΝϨϯεͱ͸ผྉۚ : €649 •

    9:00 ʙ 17:00 • ே͝͸Μɾன͝͸Μ͋Γ • ձ৔͸ΧϯϑΝϨϯεͱಉ͡ • https://kotlinconf.com/workshops/
  2. Workshops • Up and running with Kotlin by Svetlana Isakova

    • Asynchronous Programming with Kotlin by Roman Elizarov • Developing Android Apps in Kotlin by Florina Muntenescu and Sean McQuillan • Building a Full Stack Web Application in Kotlin by Garth Gilmour • Refactoring to Kotlin by Duncan McGregor and Nat Pryce
  3. Asynchronous Programming with Kotlin • ߨࢣ : Roman Elizarov •

    https://github.com/elizarov • ಺༰ : coroutines
  4. ࣄલ४උ • ࠷৽ͷ IntelliJ IDEA (CE Մ) ΛΠϯετʔϧ͓ͯ͘͠ • Kotlin

    plugin ͷ࠷৽ͷ EAP 1.3 version ΛΠϯετʔϧ͓ͯ͘͠ • [Tools] → [Kotlin] → [Con fi gure Kotlin Plugin Updates] → Early Access Preview 1.3 → Check & Install the latest version • Github ϩάΠϯ༻ͷτʔΫϯ • [Settings] →[Developer Setting] → Personal Access Tokens → Generate New Token
  5. Outline • Asynchronous programming, Introduction to Coroutines • Coroutines vs

    Threads, Context and Cancellation • Coroutines and Concurrency • CSP with Channels and Actors
  6. αϯϓϧϓϩδΣΫτ • project/ContributorsUI.main() • σεΫτοϓΞϓϦʢSwingར༻ʣ • GitHub ͔Β kotlin Organization

    ͷϦϙδτϦҰཡΛऔ ಘ͠ɺ֤ϦϙδτϦͷ contributors Λऔಘ͠ूܭ͢Δ • part1async/ ʙ part4csp/ • খ͍͞ demo ίʔυू
  7. • Kotlin Organization ͷϦϙδτϦΛऔಘ • List<Repo> • ϦϙδτϦ͝ͱ Contributors Λऔಘ

    • List<User> • ϢʔβʔͷॏෳΛ fl attenͯ͠ूܭ Repo1 - User11 - User12 - … Repo2 - User21 - User22 - … …
  8. enum class Variant { BLOCKING, // Request1Blocking BACKGROUND, // Request2Background

    CALLBACKS, // Request3Callbacks COROUTINE, // Request4Coroutine PROGRESS, // Request5Progress CANCELLABLE, // Request5Progress (too) CONCURRENT, // Request6Concurrent GATHER, // Request8Gather ACTOR // Request9Actor }
  9. Programming styles • 1. Blocking threads • 2. Callbacks •

    3. Futures • 4. Kotlin coroutines COROUTINE, // Request4Coroutine PROGRESS, // Request5Progress CANCELLABLE, // Request5Progress (t CONCURRENT, // Request6Concurrent GATHER, // Request8Gather ACTOR // Request9Actor FUTURE, // Request7Future CALLBACKS, // Request3Callbacks BACKGROUND, // Request2Background
  10. ΢ΥʔϛϯάΞοϓ data class User( val login: String, val contributions: Int

    ) fun List<User>.aggregate(): List<User> = TODO List<User> ͷத͔Βॏෳͨ͠ login ͷ User Λ̍ͭʹ·ͱΊΔ ·ͱΊΔͱ͖ contributions Λ଍͠߹ΘͤΔ contributions ͷଟ͍ॱʹιʔτ͢Δ
  11. ΢ΥʔϛϯάΞοϓ fun List<User>.aggregate(): List<User> = groupingBy { it.login } .reduce

    { login, a, b -> User(login, a.contributions + b.contributions) } .values .sortedByDescending { it.contributions } data class User( val login: String, val contributions: Int ) List<User> ͷத͔Βॏෳͨ͠ login ͷ User Λ̍ͭʹ·ͱΊΔ ·ͱΊΔͱ͖ contributions Λ଍͠߹ΘͤΔ contributions ͷଟ͍ॱʹιʔτ͢Δ
  12. Background fun loadContributorsBackground(req: RequestData, callback: (List<User>) -> Unit) { thread

    { val users = loadContributorsBlocking(req) callback(users) } } εϨουͰ
  13. Callbacks fun loadContributorsCallbacks(req: RequestData, callback: (List<User>) -> Unit) { val

    service = createGitHubService(req.username, req.password) service.listOrgRepos(req.org).responseCallback { repos -> … } } inline fun <T> Call<T>.responseCallback(crossinline callback: (T) -> Unit) { enqueue(object : Callback<T> { override fun onResponse(call: Call<T>, response: Response<T>) { checkResponse(response) callback(response.body()!!) } override fun onFailure(call: Call<T>, t: Throwable) { log.error("Call failed", t) } }) } Retro fi t ͷ enqueue Λར༻
  14. Coroutines launch { val users = loadContributors(req) updateResults(users) } suspend

    fun loadContributors(req: RequestData) : List<User> { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val contribs = repos.flatMap { repo -> val users = service.listRepoContributors(req.org, repo.name).await() users }.aggregate() return contribs } loadContributors() ෦෼͸ blocking ͱಉ͡
  15. Progress suspend fun loadContributorsProgress(req: RequestData, callback: (List<User>) -> Unit) {

    val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() var contribs = listOf<User>() for (repo in repos) { val users = service.listRepoContributors(req.org, repo.name).await() contribs = (contribs + users).aggregateSlow() callback(contribs) } } Coroutines + Progress ϦϙδτϦϩʔυ͝ͱʹ callback ݺͼग़͠
  16. Cancellable private fun Job.updateCancelJob() { updateEnabled(false) val listener = ActionListener

    { cancel() } cancel.addActionListener(listener) launch { join() updateEnabled(true) cancel.removeActionListener(listener) } } ΩϟϯηϧϘλϯ͕ԡ͞ΕͨΒ Job Λ cancel() launch { loadContributorsProgress(req) { users -> updateResults(users) } }.updateCancelJob()
  17. Concurrent suspend fun loadContributorsConcurrent(req: RequestData): List<User> = coroutineScope { val

    service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val contribs = repos.map { repo -> async { val users = service.listRepoContributors(req.org, repo.name).await() users } }.awaitAll().flatten().aggregate() contribs } async ͰϦϙδτϦ͝ͱͷ Contributors ϩʔυΛฒྻԽ
  18. Future val future = loadContributorsConcurrentAsync(req) updateCancelFuture(future) future.thenAccept { users ->

    SwingUtilities.invokeLater { updateResults(users) } } fun loadContributorsConcurrentAsync( req: RequestData ): CompletableFuture<List<User>> = GlobalScope.future { loadContributorsConcurrent(req) }
  19. Gather suspend fun loadContributorsGather( req: RequestData, callback: suspend (List<User>) ->

    Unit ) = coroutineScope { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val channel = Channel<List<User>>() for (repo in repos) { launch { val users = service.listRepoContributors(req.org, repo.name).await() channel.send(users) } } var contribs = emptyList<User>() repeat(repos.size) { val users = channel.receive() contribs = (contribs + users).aggregateSlow() callback(contribs) } } Channel Λ࢖༻
  20. Actor suspend fun loadContributorsActor( req: RequestData, uiUpdateActor: SendChannel<List<User>> ) =

    coroutineScope<Unit> { val service = createGitHubService(req.username, req.password) val repos = service.listOrgRepos(req.org).await() val aggregator = aggregatorActor(uiUpdateActor) val requests = Channel<WorkerRequest>() val workers = List(4) { workerJob(requests, aggregator) } for (repo in repos) { requests.send(WorkerRequest(service, req.org, repo.name)) } requests.close() workers.joinAll() aggregator.close() } List<Repo> → requests : Channel<WorkerRequest> → aggregator: SendChannel<List<User>> → uiUpdateActor : SendChannel<List<User>> 4ฒྻͰWorkRequestΛॲཧ
  21. fun CoroutineScope.aggregatorActor( uiUpdateActor: SendChannel<List<User>> ) = actor<List<User>> { var contribs:

    List<User> = emptyList() // STATE for (users in channel) { contribs = (contribs + users).aggregateSlow() uiUpdateActor.send(contribs) } } class WorkerRequest( val service: GitHubService, val org: String, val repo: String ) fun CoroutineScope.workerJob( requests: ReceiveChannel<WorkerRequest>, aggregator: SendChannel<List<User>> ) = launch { for (req in requests) { val users = req.service.listRepoContributors(req.org, req.repo).await() log.info("${req.repo}: loaded ${users.size} contributors") aggregator.send(users) } } actor { } Λ࢖༻
  22. Have a Nice Kotlin! • blog : Y.A.M ͷࡶهா •

    y-anz-m.blogspot.com • twitter : @yanzm ʢ΍Μ͟Ήʣ • uPhyca Inc. (גࣜձࣾ΢ϑΟΧ)