views:

210

answers:

3

This is a follow-up now that Scala 2.8.0 beta is out to this question:

http://stackoverflow.com/questions/1710813/what-is-a-proper-way-to-manage-flexible-typed-immutable-data-structures-in-scal

The new technique is to copy a case class, e.g.

case class Person(name:String, email:String)

val bob = Person("Bob", "[email protected]")
val jill = bob.copy(name = "Jill")

This works great, except for the fact that Scala seems to limit me to 22 (?) properties in a case class. That might seem like a lot, it's insufficient in my case.

With 23, I get: "error: type Function23 is not a member of package scala". I could possibly define my own Function23, etc., but I don't know the implications of that.

So now I'm back to square one. I need to use public vars, which I'm trying to avoid, or create a 26+ parameter constructor and a paired copy method. Ick.

22 seems like a rather arbitrary limit here. Is there a way around this?

This is for data import, which looks something like this:

new CatalogImportRecord() {
    override val List(SVal(vendorSku), SVal(title), IVal(issues),
      _, // YToMVal(termMonths),
      DVal(sellPrice), DVal(buyPrice), DVal(retailPrice), NotesVal(allowsNew, allowsRenewals),
      _) //DateValMdy(lastUpdated))
      = fields

You can see I commented out unused extractions to reduce the number of fields.

Maybe there's a better way to do this. I find this extraction technique a bit rigid, but that might be for the best.

+3  A: 

The code is autogenerated, so you could recompile the Scala library (with settings changed) if you were only going to use the project yourself.

If you are typing out 23+ things by hand in case classes, consider grouping your values in any way that's convenient--use tuples to group things that make sense together, or use sub-case-classes to group things more tightly. It will make updating a little more awkward, but if you group things that need to be updated together it should help.

For example, try this out in Scala 2.8:

((1,2,3,4,5,6,7,8,9),(1,2,3,4,5,6),(1,2,3,4,5,6,7,8,9)).copy(_2=(4,5,6,7,8,9))

If you've already got the original in a val, it's even easier to change just one thing:

val a = (1,2,(1,2))
a.copy(_3=a._3.copy(_1=3))
Rex Kerr
I don't know about tuples - a.copy(_3=a._3.copy(_1=3)) would send a lot of people running, but perhaps sub-case-classes is a good solution.
Alex Neth
Tuples are just pre-defined case classes. It's a lot clearer with your own code--it'd look more like `record.copy(customer=record.customer(lastname="Smith"))`.
Rex Kerr
+1  A: 

Are you writing these gargantuan case classes manually?

If you're synthesizing them programmatically, then you can always replicate what the compiler does for case classes and get essentially the same result without the Product22 limitation. Everything it does for case classes has an equivalent in Scala source for a non-case class.

Randall Schulz
I wouldn't say they are gargantuan... and it's just one class. But I don't like the idea of writing a constructor and copy method to keep in sync.
Alex Neth
+2  A: 

See genprod.scala in the src/build directory, modify it, use it to generate the desired arity, and then recompile the library. You'll probably need to keep a modified Scala library.

Perhaps it is possible to separate those extra classes, compile them, and keep them in another jar file. I don't know, but it would be worth a try, I expect.

Or use a Map instead of a case class.

Daniel
Thanks for the suggestion. I don't want to use a map (lose type-safety which is half the point of Scala). I also don't want to keep my own core scala library. As it stands, I just reduced the number of fields. It's a strange limitation in the language... Overall, though, Scala 2.8 is a massive improvement in usability with default and named parameters.
Alex Neth