Reputation: 3346
Right now I have classes like:
abstract class Record {
// Required fields
val productCode:Option[String]
val price:Option[Double]
// Optional fields
val notes:Option[String] = None
val used:Option[Boolean] = Option(false)
}
Then create them:
val r = new Record {
override val productCode = Option("abc")
override val price = Option(32.12)
}
A few things to note:
This makes mass assignment (which I'm doing because I have an array of fields) difficult if a subclass mixes new fields in, e.g.:
override val List(productCode, price, discount) = fields // fields is a List
will not compile because discount
is not defined in the superclass and therefor not an override. I'm not sure if there is a way to do this.
My main question is:
e.g. (pseudocode}:
val r2 = r.clone { override val used = true }
I have heard 2.8 introduces something like this for case classes, however in a language that encourages immutable data structures, I'd be surprised to find out this is not easier before 2.8. I'm still in 2.7.
Upvotes: 3
Views: 422
Reputation: 22660
Lenses are a great tool to operate on immutable data structures. See this question.
Upvotes: 0
Reputation: 8935
Well as already said there is no straight forward way in current ( 2.7) Scala to do that, but from my point of view it can be done pretty easily with builder pattern. Code to demonstrate:
abstract class Record {
// Required fields
val productCode:Option[String]
val price:Option[Double]
// Optional fields
val notes:Option[String] = None
val used:Option[Boolean] = Option(false)
}
class RecordBuilder{
private var _productCode:Option[String] = null
private var _price:Option[Double] = null
// Optional fields
private var _notes:Option[String] = None
private var _used:Option[Boolean] = Option(false)
def productCode(in:Option[String]) : RecordBuilder = {
_productCode = in
this
}
def price(in : Option[Double]) : RecordBuilder = {
_price = in
this
}
def notes(in:Option[String]) : RecordBuilder = {
_notes = in
this
}
def used (in : Option[Boolean]) : RecordBuilder = {
_used = in
this
}
def create() : Record = {
val r = new Record = {
override productCode = _productCode
override price = _price
override notes = _notes
override used = _used
}
r
}
}
object Record{
def from(in:Record) : RecordBuilder = {
val r = new RecordBuilder
r.productCode(in.productCode).price(in.price).notes(in.notes)
.used(in.used)
}
}
object Test {
def main(args:Array[String]) = {
val r = new Record {
override val productCode = Option("abc")
override val price = Option(32.12)}
}
val r1 = Record.from(r).used(true).create
}
Upvotes: 0
Reputation: 134340
Using Option
for a field which is not optional seems insane to me: why?
Upvotes: 0
Reputation: 10577
This looks to be a problem very much addressed in 2.8:
case class Employee(name: String, age: Int)
val joe = Employee("Joe", 25)
val bob = joe copy (name = "Bob")
Combine this with default values, and the example you give can be easily rewritten as a case class, which I think of as the 'proper' way to implement an immutable data type. (I'm not sure if that's true for scala, but coming from ocaml/haskell, it seems right.)
In 2.7 you're going to have to implement a whole lot of helper functions:
def asUsed(r: Record): Record = {
Record(r.productCode, r.price, r.nodes, Some(true))
}
Yuck. They should really hurry along 2.8...
Upvotes: 2
Reputation: 297285
There is no easy way to clone instances. FWIW, immutable data structures are usually deep. For instance, the List
class has only two members: hd
and tl
. A list grows by chaining members.
You clone such structures by creating the minimum amount of new data structures, and refencing as much of the old data structure as possible. Usually, this is done through recursion.
You learn more about this in the book Purely Functional Data Structures. The paper on which the book is based is freely available.
You can look up Scala questions here to see interesting ways to handle Option data. Unfortunately, I don't have any solutions to your other concerns.
Upvotes: 2