Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
ミュータビリティと
イミュータビリティの狭間
関数型言語使いから見たKotlinコレクション
#kotlinfest2024_after_33
1
のプロダクトエンジニア
主要技術スタック: Kotlin +
のたまごスポンサー🥚
の運営企業
関数型言語/関数型プログラミングが好き
仕事や趣味で , , などに長く
触れてきた
最近再入門したKotlinも好きになりつつある😆
lagénorhynque🐬カマイルカ
株式会社スマートラウンド
Ktor
Kotlin Fest 2024
Server-Side Kotlin Meetup
Clojure Scala Haskell
2
1. きっかけ
2. 実験・調査
3. まとめ
3
1. きっかけ
4
Kotlin Fest 2024のセッションを聴いていた私🐬
「Kotlinにはイミュータブルとミュータブルの2種
類のコレクションがあります」
「Kotlinを導入することで私たちはイミュータビリ
ティを得ました」
Kotlinの List のコードとともに破壊的更新操作
ができない例が示される
5
🐬の反応
Kotlinの List, Set, Map などは「イミュータブル」
(不変)なコレクションといえるのだろうか🤔
同じくJVM言語(さらに関数型言語)のScalaや
Clojureとはどう違うのだろう
確かKotlinには準標準ライブラリ(?)として
があったはず🤔
immutable/persistent collectionsを別途用意し
ているということは標準のコレクションはそうで
はないと暗示しているような?
kotlinx.collections.immutable
6
2. 実験・調査
7
Lisper/Clojurianの本能に従ってREPLで試してみる
ℹ️実行環境
# Kotlin
$ kotlinc -version
info: kotlinc-jvm 2.0.0 (JRE 22.0.1)
# Scala
$ scala -version
Scala code runner version 3.4.2 -- Copyright 2002-2024, LAMP/
EPFL
# Clojure
$ clj -version
Clojure CLI version 1.11.3.1463
8
Kotlinの List
MutableList インスタンスを List 変数に代入する:
>>> val xs: MutableList<Int> = mutableListOf(1, 2, 3)
// イミュータブル(?)とされるList型の変数に代入できてしまった
>>> val ys: List<Int> = xs
>>> ys.add(4)
error: unresolved reference: add
ys.add(4)
^
>>> xs.add(4)
res3: kotlin.Boolean = true
>>> xs
res4: kotlin.collections.MutableList<kotlin.Int> = [1, 2, 3,
4]
// MutableList型の変数xsからの破壊的更新がysに必然的に波及している
>>> ys
res5: kotlin.collections.List<kotlin.Int> = [1, 2, 3, 4]
9
インターフェース
kotlin.collections.MutableList は
kotlin.collections.List を継承している
ref.
実体は (Javaのミュータ
ブルな可変長リスト)
>>> xs::class
res6: kotlin.reflect.KClass<out kotlin.collections.MutableLi
st<kotlin.Int>> = class java.util.ArrayList
>>> ys::class
res7: kotlin.reflect.KClass<out kotlin.collections.List<kotl
in.Int>> = class java.util.ArrayList
MutableList - Kotlin Programming
Language
java.util.ArrayList
10
List インスタンスをJavaのメソッドで操作する:
>>> val zs: List<Int> = listOf(1, 3, 2)
// Javaの破壊的なメソッドがコンパイル/実行時エラーなく呼び出せてしまった
>>> java.util.Collections.sort(zs, { a, b -> b.compareTo(a) }
)
// イミュータブル(?)とされるListのインスタンスが更新できてしまった
>>> zs
res10: kotlin.collections.List<kotlin.Int> = [3, 2, 1]
11
listOf に2要素以上を与えて得られる実体は
の戻り値と同じ(ミ
ュータブルな固定長リスト)
>>> listOf<Int>()::class
res11: kotlin.reflect.KClass<out kotlin.collections.List<kotl
in.Int>> = class kotlin.collections.EmptyList
>>> listOf(1)::class
res12: kotlin.reflect.KClass<out kotlin.collections.List<kotl
in.Int>> = class java.util.Collections$SingletonList
>>> listOf(1, 2)::class
res13: kotlin.reflect.KClass<out kotlin.collections.List<kotl
in.Int>> = class java.util.Arrays$ArrayList
>>> listOf(1, 2, 3)::class
res14: kotlin.reflect.KClass<out kotlin.collections.List<kotl
in.Int>> = class java.util.Arrays$ArrayList
java.util.Arrays.asList
12
参考: Scalaの List
ListBuffer インスタンスを List 変数に代入する:
が明確に分離されている
scala> val xs: List[Int] = List(1, 2, 3)
val xs: List[Int] = List(1, 2, 3)
// Listは独自のイミュータブルコレクション
scala> xs.getClass
val res0: Class[? <: List[Int]] = class scala.collection.immu
table.$colon$colon
// ミュータブルなListBufferとイミュータブルなListにまったく互換性なし
scala> val ys: scala.collection.mutable.ListBuffer[Int] = xs
-- [E007] Type Mismatch Error: ------------------------------
// (エラー詳細は省略)
ミュータブルコレクションとイミュータブルコレク
ションの型階層
13
List インスタンスをJavaのメソッドで操作する:
が、破壊的更新はできない
(できたらイミュータブルコレクションと呼べない)
scala> val zs: List[Int] = List(1, 3, 2)
val zs: List[Int] = List(1, 3, 2)
// ScalaのListはJavaのCollection/Listと互換性がない
scala> java.util.Collections.sort(zs, Ordering[Int].reverse)
-- [E007] Type Mismatch Error: ------------------------------
// (エラー詳細は省略)
// 内部的な実体を維持しながら(コピーせず) Javaコレクションに変換できる
scala> import scala.jdk.CollectionConverters.*
scala> val zs_ = zs.asJava
val zs_: java.util.List[Int] = [1, 3, 2]
// 破壊的更新操作はサポートしていない(実行時エラーになる)
scala> java.util.Collections.sort(zs_, Ordering[Int].reverse)
java.lang.UnsupportedOperationException
実体を維持したままJavaのインターフェースに適合
するように変換できる
14
参考: Clojureのvector (IPersistentVector)
標準ライブラリに
user=> (def xs [1 2 3])
#'user/xs
;; vectorは独自のイミュータブルコレクション
user=> (class xs)
clojure.lang.PersistentVector
;; IPersistentVectorインターフェースを実装している
user=> (instance? clojure.lang.IPersistentVector xs)
true
;; Java interopを円滑にするため、java.util.Listも実装している
user=> (instance? java.util.List xs)
true
独自のミュータブルコレクション
はない
15
vectorインスタンスをJavaのメソッドで操作する:
Javaのインターフェースを実装しているが、破壊的
更新はできない(できたらイミュータブルコレクシ
ョンと呼べない)
user=> (def zs [1 3 2])
#'user/zs
;; 破壊的更新操作はサポートしていない(実行時エラーになる)
user=> (java.util.Collections/sort zs >)
Execution error (UnsupportedOperationException) at java.util.
List/sort (List.java:514).
16
3. まとめ
17
イミュータブル(不変)っぽく見えてイミュータブル
とは言いがたいデータ構造がある
Kotlinの List はその例だといえる
immutableではなく read-only (読み取り専用)
なインターフェースと呼ぶのが正確と思われる
ref.
effectively immutable (実質的にイミュータブル)
だと形容されることもあるが、抜け道が残れば当
然ながらイミュータビリティ(不変性)は保証され
ないことになる
List - Kotlin Programming Language
18
🐬の感想:
Kotlin言語/標準ライブラリの設計判断には実用
上の合理性を感じる
ある意味でScala/Clojure以上にJava interopを
重視するからこそ必然性があるのかも(?)
しかし、純粋な関数とデータで副作用を最小化/
局所化するプログラミングスタイル(関数型プロ
グラミング)を好む私としては、イミュータブル
コレクションがデフォルトであってほしかった
(Kotlinでの総合的な開発体験は好印象😊)
19
Further Reading
KotlinのListについて
: REPLでの簡単な実験結果のメモ by 🐬
read-only vs immutable lists in Kotlin, Scala and
Clojure
Kotlinのコレクションは「不変」と「可変」に分か
れていない - kmizuの日記
Kotlin 紹介 - List / MutableList - PHONE APPLI
Engineer blog
20
Kotlin, Scala, Clojureのコレクションについて
Collections overview | Kotlin Documentation
Calling Java from Kotlin | Kotlin Documentation >
Mapped types
Mutable and Immutable Collections | Collections
| Scala Documentation
Conversions Between Java and Scala Collections
| Collections | Scala Documentation
Clojure - Data Structures > Collections
21
関数型プログラミングについて
: 関数型プログラミ
ングの考え方と実利の紹介 by 🐬
: 不変かつ永続的なコ
レクションの簡単な紹介 by 🐬
JavaからScala、そしてClojureへ: 実務で活きる関
数型プログラミング | ドクセル
Clojureコレクションで探るimmutableで
persistentな世界 | ドクセル
Immutable object - Wikipedia
Persistent data structure - Wikipedia
22

More Related Content

ミュータビリティとイミュータビリティの狭間: 関数型言語使いから見たKotlinコレクション