Bir vaka sınıfı örneğini klonlama ve Scala'da yalnızca bir alanı değiştirme


208

Diyelim ki kişilikleri, farklı sosyal ağlardaki insanları temsil eden bir vaka sınıfım var. Bu sınıfın örnekleri tamamen değişmezdir ve sonunda bir Akka oyuncusu tarafından değiştirilmek üzere değişmez koleksiyonlarda tutulur.

Şimdi, birçok alanı olan bir vaka sınıfı var ve ben alanlardan birini, bu gibi bir şey güncelleştirmek gerektiğini söyleyen bir ileti alıyorum:

case class Persona(serviceName  : String,
                   serviceId    : String,
                   sentMessages : Set[String])

// Somewhere deep in an actor
val newPersona = Persona(existingPersona.serviceName,
                         existingPersona.serviceId,
                         existingPersona.sentMessages + newMessage)

Yalnızca tek bir değişiklik olsa bile tüm alanları belirtmem gerekiyor. Var olanPersona'yı kopyalamanın ve değişmeyen tüm alanları belirtmeden yalnızca bir alanı değiştirmenin bir yolu var mı? Bunu bir özellik olarak yazabilir ve tüm vaka sınıflarım için kullanabilir miyim?

Persona Harita benzeri bir örnek olsaydı, bunu yapmak kolay olurdu.

Yanıtlar:


324

case classcopytam olarak bu kullanıma adanmış bir yöntemle gelir :

val newPersona = existingPersona.copy(sentMessages = 
                   existingPersona.sentMessages + newMessage)

5
Nerede belgeleniyor? Örneğin " scala-lang.org/api/current/index.html " açık "noktalarında kopyalanacak bir referans bulamıyorum .
François Beausoleil

6
Bu dilin bir özelliği, Scala spesifikasyonunda bulabilirsiniz: scala-lang.org/docu/files/ScalaReference.pdf §5.3.2. API'da değil çünkü API'nin bir parçası değil;)
Nicolas

1
ScalaDoc'un kopyalama yöntemlerini mevcut olduklarında göstermesini sağlamayı amaçladım, istediğin bu değil mi?
soc

4
Güzel olurdu. Ama burada, François'in problemi (eğer haklıysam), copyeğer bir beyanda bulunursa bir yöntemi olacağını bilmiyor olmasıdır case class.
Nicolas

2
@JonathanNeufeld Bu duygu ile saf fp kampında birçok arkadaş edineceksiniz. Seninle aynı fikirde olmaya meyilliyim.
javadba

46

2.8'den beri, Scala vaka sınıflarının copysihrini çalıştırmak için adlandırılmış / varsayılan parametrelerden yararlanan bir yöntemi vardır:

val newPersona =
  existingPersona.copy(sentMessages = existing.sentMessages + newMessage)

Ayrıca Personakullanımı basitleştirmek için bir yöntem de oluşturabilirsiniz :

case class Persona(
  svcName  : String,
  svcId    : String,
  sentMsgs : Set[String]
) {
  def plusMsg(msg: String) = this.copy(sentMsgs = this.sentMsgs + msg)
}

sonra

val newPersona = existingPersona plusMsg newMsg


0

Kullanmayı düşünün lensiçinde Shapelesskütüphaneye:

import shapeless.lens

case class Persona(serviceName  : String,
                   serviceId    : String,
                   sentMessages : Set[String])
// define the lens
val messageLens = lens[Persona] >> 'sentMessages 

val existingPersona = Persona("store", "apple", Set("iPhone"))

// When you need the new copy, by setting the value,
val newPersona1 = messageLens.set(existingPersona)(Set.empty)
// or by other operation based on current value.
val newPersona2 = messageLens.modify(existingPersona)(_ + "iPad")

// Results:
// newPersona1: Persona(store,apple,Set())
// newPersona2: Persona(store,apple,Set(iPhone, iPad))

Ayrıca, iç içe vaka sınıflarınız varsa, gettervesetter yöntemleri oluşturmak biraz sıkıcı olabilir. Lens kütüphanesini kullanarak basitleştirmek için iyi bir şans olacaktır.

Lütfen ayrıca bakınız:


0

Yuvalanmış vaka sınıflarında değerleri derinlemesine ayarlamanızı sağlayan karmaşık lensler yapmak için büyük bir kütüphane eklemek istemedim. Skala kütüphanesinde sadece birkaç satır kod olduğu ortaya çıktı:

  /** http://stackoverflow.com/a/5597750/329496 */
  case class Lens[A, B](get: A => B, set: (A, B) => A) extends ((A) => B) with Immutable {
    def apply(whole: A): B = get(whole)

    def mod(a: A, f: B => B) = set(a, f(this (a)))

    def compose[C](that: Lens[C, A]) = Lens[C, B](
      c => this(that(c)),
      (c, b) => that.mod(c, set(_, b))
    )

    def andThen[C](that: Lens[B, C]) = that compose this
  }

Daha sonra, derin yuvalanmış değerleri yerleşik kopyalama özelliğini kullanmaktan çok daha kolay ayarlayan lensler oluşturabilirsiniz. Kitaplığımın yoğun şekilde iç içe değerleri ayarlamak için kullandığı karmaşık lensler varsa, büyük bir kümeye bağlantı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.