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

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

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
Tweet

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. (גࣜձࣾ΢ϑΟΧ)