Skip to content

Instantly share code, notes, and snippets.

@lagenorhynque
Last active July 8, 2024 01:59
Show Gist options
  • Save lagenorhynque/0e96d7f853a97ddf4d5e4cb8b45f587f to your computer and use it in GitHub Desktop.
Save lagenorhynque/0e96d7f853a97ddf4d5e4cb8b45f587f to your computer and use it in GitHub Desktop.
read-only vs immutable lists in Kotlin, Scala and Clojure
user=> (def xs [1 2 3])
#'user/xs
;; Clojureの `[ ]` リテラルで得られるvector (シーケンシャルコレクションの一種)実装は独自のイミュータブルコレクション
user=> (class xs)
clojure.lang.PersistentVector
;; java.util.Listを実装している
user=> (instance? java.util.List xs)
true
;; 破壊的更新操作は実装されていないため実行時エラーになる
user=> (java.util.Collections/sort xs >)
Execution error (UnsupportedOperationException) at java.util.List/sort (List.java:514).
null
;; 例えば元のデータからJavaのArrayListを作れば破壊的更新ができるが、元のデータに影響することはない
user=> (def xs' (java.util.ArrayList. xs))
#'user/xs'
user=> xs'
[1 2 3]
user=> (java.util.Collections/sort xs' >)
nil
user=> xs'
[3 2 1]
user=> xs
[1 2 3]
>>> val xs: List<Int> = listOf(1, 2, 3)
// Kotlinの `listOf( )` で得られるList実装はjava.util.Arrays.asListで得られるものと同じ(ミュータブルな固定長リスト)
>>> xs::class
res1: kotlin.reflect.KClass<out kotlin.collections.List<kotlin.Int>> = class java.util.Arrays$ArrayList
// 実行時のオブジェクトはjava.util.Listを実装している(ただし、Kotlinとしてのインターフェースkotlin.collections.Listはjava.util.Listを実装していない)
// cf. Kotlinのmapped types: https://kotlinlang.org/docs/java-interop.html#mapped-types
>>> xs is java.util.List<*>
res2: kotlin.Boolean = true
// 破壊的なソート処理によって元のデータが更新できてしまう(イミュータブルコレクションではないことが分かる)
>>> java.util.Collections.sort(xs, { a, b -> b.compareTo(a) })
>>> xs
res4: kotlin.collections.List<kotlin.Int> = [3, 2, 1]
scala> val xs: List[Int] = List(1, 2, 3)
val xs: List[Int] = List(1, 2, 3)
// Scalaの `List( )` で得られるList実装は独自のイミュータブルコレクション
scala> xs.getClass
val res0: Class[? <: List[Int]] = class scala.collection.immutable.$colon$colon
// java.util.Listを実装していない
scala> xs.isInstanceOf[java.util.List[?]]
val res1: Boolean = false
// JavaのListインターフェースを実装していないためコンパイルエラーになる
scala> java.util.Collections.sort(xs, Ordering[Int].reverse)
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |java.util.Collections.sort(xs, Ordering[Int].reverse)
| ^^
| Found: (xs : List[Int])
| Required: java.util.List²[Int]
|
| where: List is a class in package scala.collection.immutable
| List² is a trait in package java.util
|
| longer explanation available when compiling with `-explain`
1 error found
// JavaのList実装に変換しても破壊的更新操作は実装されていないため実行時エラーになる
scala> import scala.jdk.CollectionConverters.*
scala> val xs_ = xs.asJava
val xs_: java.util.List[Int] = [1, 2, 3]
scala> java.util.Collections.sort(xs_, Ordering[Int].reverse)
java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.set(AbstractList.java:136)
at java.base/java.util.AbstractList$ListItr.set(AbstractList.java:439)
at java.base/java.util.List.sort(List.java:514)
at java.base/java.util.Collections.sort(Collections.java:179)
... 41 elided
// 例えば元のデータからJavaのArrayListを作れば破壊的更新ができるが、元のデータに影響することはない
scala> val xs2 = java.util.ArrayList(xs.asJava)
val xs2: java.util.ArrayList[Int] = [1, 2, 3]
scala> java.util.Collections.sort(xs2, Ordering[Int].reverse)
scala> xs2
val res3: java.util.ArrayList[Int] = [3, 2, 1]
scala> xs
val res4: List[Int] = List(1, 2, 3)
# REPL起動に利用するコマンドのバージョン確認
$ kotlinc -version
info: kotlinc-jvm 2.0.0 (JRE 22.0.1)
$ scala -version
Scala code runner version 3.4.2 -- Copyright 2002-2024, LAMP/EPFL
$ clj -version
Clojure CLI version 1.11.3.1463
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment