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

Kotlinコンテキストパラメータをアプリ開発で使う夢を見る

Avatar for jj1uzh jj1uzh
June 30, 2026
35

 Kotlinコンテキストパラメータをアプリ開発で使う夢を見る

Avatar for jj1uzh

jj1uzh

June 30, 2026

Transcript

  1. Kotlin コンテキストパラメータとは • 2.2(experimental) → 2.4(stable) • 関数に暗黙的に引数を渡す • 渡す条件は型が一致

    or 継承すること 3 context(c: SomeContext) fun foo(n: Int) { … c.somefunc(); … } context(someContext) { foo(123) }
  2. Kotlin コンテキストパラメータとは • 一般的な使用例1: 引数の使い回し ◦ DSL 4 context(s: Session)

    fun insertUser(u: User) { // DB操作の関数がSessionを s.insert(u.asEntity) // コンテキストから取り出す } context(newTransaction()) { insertUser(user) updateUserMetadata(user.metadata) commit() }
  3. Kotlin コンテキストパラメータとは • 一般的な使用例1: 引数の使い回し ◦ コンテキスト伝搬 5 context(logger: Logger)

    fun subHoge() { … logger.debug(…); … } context(logger: Logger) fun hoge() { … logger.info(…); subHoge(); … } // 伝搬 context(newLogger(label = “MyApp”)) { hoge() }
  4. vs Function types with receivers • A.(B) -> C ◦

    ラムダ中の thisを指定 9 fun query(body: QueryBuilder.() -> Unit) class QueryBuilder { fun select(body: SelectBuilder.() -> Unit) } class SelectBuilder { fun columns(names: List<String>) } query { it.select { it.columns(…) } }
  5. vs Function types with receivers • コンテキストパラメータで書くなら ◦ 各DSLメソッドはトップレベル関数に 10

    fun query(body: context(QueryBuilder) () -> Unit): … context(q: QueryBuilder) fun select(body: context(SelectBuilder) () -> Unit) context(s: SelectBuilder) fun columns(names: List<String>) val q = query { select { columns(…) } } QueryBuilder
  6. vs Function types with receivers • コンテキストを自然に増やせる 11 fun query(logger:

    Logger, body: context(QueryBuilder) () -> Unit): … context(q: QueryBuilder, logger: Logger) fun select(body: context(SelectBuilder) () -> Unit) context(s: SelectBuilder, logger: Logger) fun columns(names: List<String>) val q = query { select { columns(…) } } QueryBuilder
  7. vs Function types with receivers • 暗黙のthis vs 暗黙の引数 •

    メリット ◦ コンテキストを自然に増やせる • デメリット ◦ DSL定義時の記述量は増える 12 QueryBuilder
  8. vs 動的なコンテキスト伝搬 • 例えばRoomDatabase.withTransaction ◦ CoroutineContextから動的に取り出す ◦ 関数シグネチャに現れない 13 @Query(“...”)

    fun createUser(u: UserEntity) // ??? database.withTransaction { createUser(user.asEntity) updateMetadata(user.asMetadataEntity) } QueryBuilder
  9. vs 動的なコンテキスト伝搬 • コンテキストパラメータだったら ◦ コンパイル時に解決 14 @Query(“...”) context(session: Session)

    // 見える化!!! fun createUser(u: UserEntity) fun withTx(body: context(Session) () -> Unit) { context(newTx()) { createUser(…) } } QueryBuilder
  10. vs 動的なコンテキスト伝搬 • 例えばCompositionLocal ◦ 子孫のCompose関数から取り出せる値を定義 15 @Composable fun ThemeText(t:String){…

    Theme.current …} @Composable fun AppScreen(){ ThemeText(“aaa”) } val Theme = compositionLocalOf { DefaultTheme() } CompositionLocalProvider(Theme provides LightTheme) { AppScreen() } QueryBuilder
  11. vs 動的なコンテキスト伝搬 • コンテキストパラメータでも ◦ recomposeも動く & コンパイル時解決 16 QueryBuilder

    @Composable context(theme: Theme) fun ThemeText(t:String){… theme …} @Composable context(theme: Theme) // ここも必要… fun AppScreen(){ ThemeText(“aaa”) } context(defaultTheme) { AppScreen() }
  12. vs 動的なコンテキスト伝搬 • メリット ◦ コンパイル時に解決 ◦ 関数シグネチャの明示 • デメリット

    ◦ 記述量 ▪ 渡すのは暗黙だけど定義は毎回必要 • interfaceにしてミックスインしていけば集約できる 17 QueryBuilder
  13. vs DI • classへのinjectionから→ 18 class DiaryRepository @Inject constructor(db: DB,

    network: DiaryNetwork){ fun publish(diary: Diary) { db.deleteDraft(diary.id) network.publishDiary(diary.data) } }
  14. vs DI • →関数ごとに依存する処理を明示 ◦ 最小権限、テストしやすさ 19 fun interface DeleteDraft

    { fun deleteDraft(id: Int) } context(d: DeleteDraft, p: PublishDiary) fun publish(diary: Diary) { d.deleteDraft(diary.id) p.publishDiary(diary.data) }
  15. 参考資料(2026-06-16閲覧) • Language Guide / Context parameters https://kotlinlang.org/docs/context-parameters.html • Kotlin

    2.2が切り拓く: コンテキストパラメータで書く関数型DSL と新しい依存管理のかたち https://speakerdeck.com/knih/context-parameters 24