Имеет ли Scala синтаксис обновления записи для создания модифицированных клонов неизменяемых структур данных?

В Mercury я могу использовать:

A = B^some_field := SomeValue

связать A с копией B, за исключением того, что some_field является SomeValue вместо того, что было в B. Я считаю, что эквивалент Haskell выглядит примерно так:

a = b { some_field = some_value }

Есть ли у Scala что-то подобное для "модификации" неизменяемых значений. Альтернативой, похоже, является конструктор, который непосредственно устанавливает каждое поле в экземпляре, что не всегда идеально (если есть инварианты, которые должен поддерживать конструктор). Плюс это было бы действительно неуклюже и намного более хрупким, если бы я должен явно передать любое другое значение в экземпляре, в котором я хочу иметь измененную копию.

Я ничего не мог найти по этому поводу в googling или в кратком обзоре справочного руководства по языку или "Scala по примеру" (который я прочитал от начала до конца, но не впитал все тем не менее, поэтому он может быть там).

Я вижу, что эта функция может иметь некоторые странные взаимодействия с защитой и подклассами в стиле Java, хотя...

Ответы

Ответ 1

Для этого вы можете использовать классы case, но вам этого не нужно. Классы классов не являются волшебными - модификатор case просто экономит вам много ввода. Метод копирования реализуется с использованием параметров named и default. Имена те же, что и поля, а значения по умолчанию - текущие значения полей. Вот пример:

class ClassWithCopy(val field1:String, val field2:Int) {
    def copy(field1:String = this.field1, field2:Int = this.field2) = {
        new ClassWithCopy(field1,field2);
    }
}

Вы можете использовать его так же, как метод копирования для классов case. Именованные и стандартные параметры являются очень полезной функцией, а не только для методов копирования.

Ответ 2

Если вы определяете свой класс как case class, создается удобный метод copy, и вызывая его, вы можете указать с именованными параметрами новые значения для определенных полей.

scala> case class Sample(str: String, int: Int)
defined class Sample

scala> val s = Sample("text", 42)
s: Sample = Sample(text,42)

scala> val s2 = s.copy(str = "newText")
s2: Sample = Sample(newText,42)

Он даже работает с полиморфными классами case:

scala> case class Sample[T](t: T, int: Int)
defined class Sample

scala> val s = Sample("text", 42)
s: Sample[java.lang.String] = Sample(text,42)

scala> val s2 = s.copy(t = List(1,2,3), 42)
s2: Sample[List[Int]] = Sample(List(1, 2, 3),42)

Обратите внимание, что s2 имеет другой тип, чем s.

Ответ 3

Если объект, который вы планируете модифицировать, является классом case, тогда вы можете использовать метод автоматической генерации копии:

scala> val user = User(2, "Sen")
user: User = User(2,Sen)

scala> val corrected = user.copy(name = "Sean")
corrected: User = User(2,Sean)