Reputation:
I'm porting one of my C++ programs into Scala. In that project, there are hundreds of user-defined classes in an organised hierarchy. If I seal one of the top-level abstract classes, according to Scala rules, I have to put the definition of all subtypes of that class in the same file as the sealed class/trait. For one of my class hierarchies it would mean putting the definition of about 30 classes in that file.
In my C++ program these 30 classes are located in their own header and implementation files making them easier to maintain and read. I fear that if I put the definition of those 30 classes/objects in one file in my Scala application, it will make them hard to maintain and read.
The reason for sealing the class is so that I can do exhaustive searches when pattern matching on those types. Any help to point in me in the right direction with regards to organising Scala class hierarchies would be appreciated.
Upvotes: 3
Views: 426
Reputation: 167871
It's a bit of a pain to do this in separate classes, but it might be less painful than having everything in one huge file. First, you need to make sure you compile all the files together. Then, in your file where you make sure everything is sealed, you do the following:
trait GenericA { def foo: Int }
sealed trait A extends GenericA
case class B() extends A with ImplB {}
case class C() extends A with ImplC {}
...
The trick is that everything in the superclass (and it can be an abstract class instead of a trait if you wish) goes into GenericA
. But you never actually use GenericA
in your code, you just use A
. Now, you can write a bunch of separate files with each implementation, defined like so:
// File #1
trait ImplB extends GenericA { def foo = 7 }
// File #2
trait ImplC extends GenericA { def foo = 4 }
...
Now you have your implementations separated out (at least those parts which can be expressed in terms of GenericA only).
What if you need the case class parameters available also? No problem--just include those as part of the trait.
// Main file
case class D(i: Int) extends A with ImplD {}
// Separate file
trait ImplD {
def i: Int
def foo = i*3
}
It's a bit of extra work since you have to repeat the case class parameters in two spots, but in your case it may be worth it.
Upvotes: 3
Reputation: 22374
Assuming your classes are case-classes which have many methods (which could make your file grow), you can try to separate definition from implementation using type classes (but sometimes it could afffect compiler's performance), like:
Model.scala
sealed trait A
case class A1(a: Int) extends A
case class A2(a: Int) extends A
case class A3(a: Int, b: Int) extends A
...
case class A1(a: Int) extends A
ImplA1.scala
package org.impl
implicit class ImplA1(a: A1) {
def method1() = a.a + a.a
}
ImplA2.scala
package org.impl
implicit class ImplA2(a: A2) {
def method1() = a.a * 2
}
Usage:
import org.impl._
val a1 = new A1
a1.method1()
Upvotes: 2