---
title: Context Parameterから見る関数の暗黙的な依存について
tags: 
author: [Udomomo](https://www.docswell.com/user/Udomomo)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/9E29ZD457R.jpg?width=480
description: Server-Side Kotlin LT大会 vol.19 での発表資料です
published: July 03, 26
canonical: https://www.docswell.com/s/Udomomo/51QQLG-2026-07-03-210316
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/9E29ZD457R.jpg)

Context Parameterから見る
関数の暗黙的な依存について
@Udomomo
2026-07-03 Server-Side Kotlin LT大会 vol.19


# Page. 2

![Page Image](https://bcdn.docswell.com/page/D7Y4R2M6EM.jpg)

プロフィール
• @Udomomo
• バックエンドエンジニアとしてKotlinで
サービスの開発をしています
• 最近はオライリーの「コンピュータシステム
の理論と実装」にハマっています


# Page. 3

![Page Image](https://bcdn.docswell.com/page/VENYD831J8.jpg)

背景
• Kotlin 2.4で正式リリースとなったContext Parameter
• どういう機能かはドキュメントを見てなんとなくわかった
• でもそもそもコンテキストって…？ (うまく言葉にできない)


# Page. 4

![Page Image](https://bcdn.docswell.com/page/Y79PGK9YE3.jpg)

コンテキストにつながる概念
「Dispatch Receiver」


# Page. 5

![Page Image](https://bcdn.docswell.com/page/G78DVR9K7D.jpg)

例: 従業員の情報を出力する拡張関数
EmployeeReportRenderer の中で定義した Employee の拡張関数には、
EmployeeReportRenderer が存在するスコープ配下でのみアクセス可能。
class EmployeeReportRenderer(
private val showPersonalInfo: Boolean,
) {
fun Employee.toReportLine(): String =
buildString {
append(&quot;$id: &quot;)
append(department)
if (showPersonalInfo) {
append(&quot; $name&lt;$email&gt;&quot;)
}
}
fun render(employees: List&lt;Employee&gt;): String =
employees.joinToString(&quot;\n&quot;) {
it.toReportLine()
}
}
// ✅OK
employeeReportRenderer.render(
listOf(employee)
)
// ✅OK
with(employeeReportRenderer) {
println(employee.toReportLine())
}
// ❌NG
employee.toReportLine()


# Page. 6

![Page Image](https://bcdn.docswell.com/page/L7LM5KWPJR.jpg)

拡張関数とDispatch Receiver
あるクラスAの拡張関数が別のクラスB内で定義された時、Bのインスタンスは
Dispatch Receiverとなり、拡張関数内で明示されずに利用される。
・拡張関数の直接の対象。先ほどの例では Employee がExtension Receiver。
Extension Receiver
・Employee.toReportLine() や it.toReportLine() のように、レシーバを明示的
に指定して呼んでおり、明示的レシーバの一種といえる。
• 拡張関数が定義されたクラスのインスタンス。先ほどの例では
EmployeeReportRenderer がDispatchReceiver。
Dispatch Receiver
• toReportLine() の中では、 EmployeeReportRenderer のプロパティである
showPersonalInfo を使っているが、レシーバを明示せずに呼んでいる。これは
暗黙的レシーバの一種であり、コンテキストの概念につながる。


# Page. 7

![Page Image](https://bcdn.docswell.com/page/4EMY3292EW.jpg)

暗黙的な依存としてのコンテキスト
多くの関数の処理結果に関わるが、直接の処理対象ではない関心事というのが
しばしば存在する。(ロギング、ユーザ解決、権限確認など…)
これらをコンテキスト(文脈) と呼び、関数の暗黙的な依存とみなすことも
できる。
Dispatch Receiverも関数から暗黙的に利用されているため、コンテキストに
近い性質があるが、あくまで特定のクラスのメンバとしてしか定義ができず、
柔軟に扱うのには限界がある。


# Page. 8

![Page Image](https://bcdn.docswell.com/page/PER9QMG5J9.jpg)

Context Receiver


# Page. 9

![Page Image](https://bcdn.docswell.com/page/P7XQ5VXXEX.jpg)

Context Receiver
• Kotlin 1.6.20でプロトタイプとして導入
• 関数に context(…) を付与することで、指定した型が関数の暗黙的な依存と
して振る舞う
• 指定したcontextは、内側のスコープにも引き継がれていく
• そのため、interface型を関数のcontextに指定し、さらに外側で指定した
contextによって解決させることも可能


# Page. 10

![Page Image](https://bcdn.docswell.com/page/37K96QW97D.jpg)

Context Receiverの例
Contextとして渡す用のinterface。先ほどの例でEmployeeReportRenderer
の中にあったshowPersonalInfoを、今度はこの中で定義する。
interface EmployeeReportPolicy {
val showPersonalInfo: Boolean
}
class ExecutivePersonalInfoPolicy (
override val showPersonalInfo: Boolean,
): EmployeeReportPolicy


# Page. 11

![Page Image](https://bcdn.docswell.com/page/LJ3W6P19J5.jpg)

Context Receiverの例
showPersonalInfo をcontextとして外部から渡すことが可能になった。
class EmployeeReportRenderer {
context(EmployeeReportPolicy)
fun Employee.toReportLine(): String =
buildString {
append(&quot;$id: &quot;)
append(department)
if (showPersonalInfo) {
append(&quot; $name&lt;$email&gt;&quot;)
}
}
context(EmployeeReportPolicy)
fun render(employees: List&lt;Employee&gt;): String =
employees.joinToString(&quot;\n&quot;) {
it.toReportLine()
}
}


# Page. 12

![Page Image](https://bcdn.docswell.com/page/8JDKVLX8EG.jpg)

Context Receiverの例
Contextとして渡されたinterfaceの実装は、スコープを上って解決される。
val employee = Employee(“001”, “email@example.com”, “name”, “department”)
val employeeReportRenderer = EmployeeReportRenderer()
val executivePersonalInfoPolicy = ExecutivePersonalInfoPolicy(showPersonalInfo = false)
with(executivePersonalInfoPolicy) {
println(employeeReportRenderer.render(listOf(employee)))
}


# Page. 13

![Page Image](https://bcdn.docswell.com/page/VEPKV2PW78.jpg)

Context Receiverの問題と
Context Parameter


# Page. 14

![Page Image](https://bcdn.docswell.com/page/27VVZD297Q.jpg)

Context Receiverの問題点: スコープ汚染
• Contextを考慮しなければ、スコープの内外関係 = ネストの内外関係
• しかしcontextがこの位置にある以上、ネストの順番とスコープが等しくなく
なる
• スコープを登って行く間、最初はContextは無視され、最後に考慮される
class A {
context(B1, B2)
fun C.f() {
// A group: [B1, B2]
with (d) { // Add receiver to the scope
// Resolution order: d -&gt; C -&gt; A -&gt; [B1, B2] -&gt; imports
}
}
}
https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md#resolution-algorithm


# Page. 15

![Page Image](https://bcdn.docswell.com/page/5JGL4YR47L.jpg)

Context Receiverの問題点: スコープ汚染
• Context Receiverがない状態でさえ、1つのネストレベルに1個のImplicit
Receiverが存在しうる
• ここにContext Receiverが加わると、全ネストレベルのImplicit Receiverの
数が無制限になり、定義元のスコープの解決が困難になりうる
class AClass { // this: AClass
fun Extension.doSomething() { // this: Extension
dsl1 { // this: Builder1
dsl2 { // this: Builder2
foo() // where is this function declared?
}
}
}
}
https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md#context-receivers-abuse-and-scope-pollution


# Page. 16

![Page Image](https://bcdn.docswell.com/page/47QYGMVREP.jpg)

Context Parameter
• Kotlin 2.4.0でStableリリース (※ 一部の付随機能はExperimental)
• 暗黙のスコープの利用を抑える設計に変更
• 関数内から参照させたい場合はパラメータ名が必須
• Context Parameterの付与を関数・プロパティ単位に限定


# Page. 17

![Page Image](https://bcdn.docswell.com/page/KE4W5VMQJ1.jpg)

Context Parameterの例
Contextにパラメータ名がつき、関数から明示的にアクセスするようになった。
class EmployeeReportRenderer {
context(policy: EmployeeReportPolicy)
fun Employee.toReportLine(): String =
buildString {
append(&quot;$id: &quot;)
append(department)
if (policy.showPersonalInfo) {
append(&quot; $name&lt;$email&gt;&quot;)
}
}
context(policy: EmployeeReportPolicy)
fun render(employees: List&lt;Employee&gt;): String =
employees.joinToString(&quot;\n&quot;) {
it.toReportLine()
}
}


# Page. 18

![Page Image](https://bcdn.docswell.com/page/L71YW38LJG.jpg)

Context Parameterの例
ただし呼び出し側は変わっていない。ExecutivePersonalInfoPolicy を
引数として渡しているわけではなく、contextの解決は暗黙的に行われる。
val employee = Employee(“001”, “email@example.com”, “name”, “department”)
val employeeReportRenderer = EmployeeReportRenderer()
val executivePersonalInfoPolicy = ExecutivePersonalInfoPolicy(showPersonalInfo = false)
with(executivePersonalInfoPolicy) {
println(employeeReportRenderer.render(listOf(employee)))
}


# Page. 19

![Page Image](https://bcdn.docswell.com/page/G7WG6PZQE2.jpg)

まとめ
• 引数に現れない暗黙的な参照 (特にDispatch Receiver) を、コンテキストと
いう概念として扱えるようにする試みがあった
• Context Receiverは、コンテキストを暗黙的なレシーバとして関数から
要求可能にする仕組みだった
• ただ定義元のスコープ解決が過度に複雑になったため、Context Parameter
では名前つきのパラメータとして扱う形に再設計された
• その結果、暗黙的なコンテキストでありつつも、関数内からは明示的に
アクセスできるという、複合的な性質を持つようになった


