Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

のプロダクトエンジニア 主要技術スタック: Kotlin + のたまごスポンサー 🥚 の運営企業 関数型言語/関数型プログラミングが好き 仕事や趣味で , , などに長く 触れてきた 最近再入門したKotlinも好きになりつつある 😆 lagénorhynque 🐬カマイルカ 株式会社スマートラウンド Ktor Kotlin Fest 2024 Server-Side Kotlin Meetup Clojure Scala Haskell 2

Slide 3

Slide 3 text

1. きっかけ 2. 実験・調査 3. まとめ 3

Slide 4

Slide 4 text

1. きっかけ 4

Slide 5

Slide 5 text

Kotlin Fest 2024のセッションを聴いていた私 🐬 「Kotlinにはイミュータブルとミュータブルの2種 類のコレクションがあります」 「Kotlinを導入することで私たちはイミュータビリ ティを得ました」 Kotlinの List のコードとともに破壊的更新操作 ができない例が示される 5

Slide 6

Slide 6 text

🐬の反応 Kotlinの List, Set, Map などは「イミュータブル」 (不変)なコレクションといえるのだろうか 🤔 同じくJVM言語(さらに関数型言語)のScalaや Clojureとはどう違うのだろう 確かKotlinには準標準ライブラリ(?)として があったはず 🤔 immutable/persistent collectionsを別途用意し ているということは標準のコレクションはそうで はないと暗示しているような? kotlinx.collections.immutable 6

Slide 7

Slide 7 text

2. 実験・調査 7

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Kotlinの List MutableList インスタンスを List 変数に代入する: >>> val xs: MutableList = mutableListOf(1, 2, 3) // イミュータブル(?)とされるList型の変数に代入できてしまった >>> val ys: List = xs >>> ys.add(4) error: unresolved reference: add ys.add(4) ^ >>> xs.add(4) res3: kotlin.Boolean = true >>> xs res4: kotlin.collections.MutableList = [1, 2, 3, 4] // MutableList型の変数xsからの破壊的更新がysに必然的に波及している >>> ys res5: kotlin.collections.List = [1, 2, 3, 4] 9

Slide 10

Slide 10 text

インターフェース kotlin.collections.MutableList は kotlin.collections.List を継承している ref. 実体は (Javaのミュータ ブルな可変長リスト) >>> xs::class res6: kotlin.reflect.KClass> = class java.util.ArrayList >>> ys::class res7: kotlin.reflect.KClass> = class java.util.ArrayList MutableList - Kotlin Programming Language java.util.ArrayList 10

Slide 11

Slide 11 text

List インスタンスをJavaのメソッドで操作する: >>> val zs: List = listOf(1, 3, 2) // Javaの破壊的なメソッドがコンパイル/実行時エラーなく呼び出せてしまった >>> java.util.Collections.sort(zs, { a, b -> b.compareTo(a) } ) // イミュータブル(?)とされるListのインスタンスが更新できてしまった >>> zs res10: kotlin.collections.List = [3, 2, 1] 11

Slide 12

Slide 12 text

listOf に2要素以上を与えて得られる実体は の戻り値と同じ(ミ ュータブルな固定長リスト) >>> listOf()::class res11: kotlin.reflect.KClass> = class kotlin.collections.EmptyList >>> listOf(1)::class res12: kotlin.reflect.KClass> = class java.util.Collections$SingletonList >>> listOf(1, 2)::class res13: kotlin.reflect.KClass> = class java.util.Arrays$ArrayList >>> listOf(1, 2, 3)::class res14: kotlin.reflect.KClass> = class java.util.Arrays$ArrayList java.util.Arrays.asList 12

Slide 13

Slide 13 text

参考: 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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

参考: 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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

3. まとめ 17

Slide 18

Slide 18 text

イミュータブル(不変)っぽく見えてイミュータブル とは言いがたいデータ構造がある Kotlinの List はその例だといえる immutableではなく read-only (読み取り専用) なインターフェースと呼ぶのが正確と思われる ref. effectively immutable (実質的にイミュータブル) だと形容されることもあるが、抜け道が残れば当 然ながらイミュータビリティ(不変性)は保証され ないことになる List - Kotlin Programming Language 18

Slide 19

Slide 19 text

🐬の感想: Kotlin言語/標準ライブラリの設計判断には実用 上の合理性を感じる ある意味でScala/Clojure以上にJava interopを 重視するからこそ必然性があるのかも(?) しかし、純粋な関数とデータで副作用を最小化/ 局所化するプログラミングスタイル(関数型プロ グラミング)を好む私としては、イミュータブル コレクションがデフォルトであってほしかった (Kotlinでの総合的な開発体験は好印象 😊) 19

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

関数型プログラミングについて : 関数型プログラミ ングの考え方と実利の紹介 by 🐬 : 不変かつ永続的なコ レクションの簡単な紹介 by 🐬 JavaからScala、そしてClojureへ: 実務で活きる関 数型プログラミング | ドクセル Clojureコレクションで探るimmutableで persistentな世界 | ドクセル Immutable object - Wikipedia Persistent data structure - Wikipedia 22