openlang

Is a read-only List<Int> a snapshot?

List<Int> is a read-only view, not a snapshot. It tells a consumer which operations it can call through that reference. It promises nothing about the collection behind it staying the same.

Take val owned = mutableListOf(2, 4). The caller passes owned to a function that accepts List<Int>, then adds 6. If the function kept that same object, it now sees the 6 as well. val fixed the reference held by owned. The contents were never fixed.

So it depends on what the function needs. If it needs the values as they were at the call, the copy has to be made at a specific point and kept from being modified afterward. I'd make that boundary explicit in the signature or its documentation. If the function is meant to read the caller's current list, holding the shared object is fine, and a copy buys nothing.

后续讨论 4

顺着彼此的依据读下去
  1. 固定链接

    On the JVM, where Kotlin can call the Java collections directly, a defensive copy at the edge of an API is cheaper than it looks. Collections.unmodifiableList(list) is only a view, so the caller's later add shows through it. List.copyOf(list) is an unmodifiable snapshot, and if you hand it a list that is already unmodifiable in that sense, one from List.of or an earlier copyOf, it generally returns the same object instead of copying. That detail is what makes the copy cheap enough to do by default.

  2. 固定链接

    Same trap in TypeScript. People read readonly number[] as a snapshot all the time, and it isn't one. A plain number[] is assignable to it, so the caller keeps a mutable handle. A slice() on the right line says "copy" better than the type does.

  3. 固定链接

    The question to settle first is when "at the call" actually is. If the function reads the list right away and returns, a copy at the top of the function is the snapshot and nothing else is needed. If it stores the list for later, a callback that runs after the caller has gone on adding things, then the copy still has to happen at the top, not where the callback finally reads it. Copying inside the callback looks defensive and captures the caller's list as it is by then, which is the thing you were trying to avoid.

    So I'd write it as two steps. Take the copy as soon as the function receives the list, then hand only the copy to whatever runs later. Python has the same shape with tuple(items) at entry, and the same mistake when the copy moves into the deferred part.

  4. 固定链接

    On the Kotlin side people mostly reach for toList(), and it is a real copy, but look at what comes back. For two or more elements it's a java.util.ArrayList behind the List type, so (snapshot as MutableList<Int>).add(8) compiles and works. The snapshot is only protected by the type. List.copyOf from your reply throws UnsupportedOperationException on that same cast and add, which is a nicer failure if anyone ever tries. For most code toList() is plenty, nobody casts their way into a snapshot by accident.

同一个问题的其他角度

  1. Annotation or satisfies for a config object

    For config objects I've mostly moved from const config: Config = {...} to {...} satisfies Config. With the annotation, config…

    回复 6最近活动 typescript
  2. Does an empty palette need its own case?

    An empty color list means two different things in a swatch editor, and Save should be able to tell them apart. Color validity…

    回复 8最近活动 fsharphaskell
  3. Validating JSON before the discriminated union

    A type assertion only tells the compiler what to believe. An external response starts as bytes and says nothing about its var…

    回复 9最近活动 typescript