Reputation: 5702
I want to slap a read and/or write trait on the Store below. To do that I need to have default null-like values for the Schemas or functions, then check them at runtime. This is to allow a read, read+write, and write only Store. The traits will define reader
and/or writer
and that is all.
This would looks like:
//read only, exception if someone tried to write with it
val rStore = new Store[SomeType](sc, readSchema) with SomeReadTrait
//write only, this would leave inSchema as null-ish so I can check if readfrom is called.
val wStore = new Store[SomeType](sc, outSchema = writeSchema) with SomeWriteTrait
// read/write
val rwStore = new Store[SomeType](sc, readSchema, writeSchema) with SomeReadTrait with SomeWriteTrait
// another way to do read/write
val rwStore = new Store[SomeType](sc, readSchema, writeSchema) with SomeReadWriteTrait
But I can't figure out how to create a default null-ish value in my constructor and a default null-ish value for an internal function def.
I might be able to handle this with three abstract types, but is there a way to do something like the following to keep the code DRY. I don't care if the check is for the reader/writer function (to see if a non null-ish one was supplied by the trait) or the Schema (to see which were given non null-ish values when constructed) or both.
abstract class Store[T](sc: SparkContext, inSchema: Schema = ????, outSchema: Schema = ????){
/** Reader function that reads from the location into the T and returns it, supplied by a Read trait*/
def reader: (SparkContext, Schema, String) => T = ????
/** Writer function, supplied in a Write trait */
def writer: (SparkContext, Schema, String, T) => ????
def readFrom(source: String): T = {
if (reader != ????) reader(sc, inSchema, source) else throw new IllegalArgumentException("Store: readFrom called but there is no reader function has been defined")
}
def writeTo(collection: T, dest: String): Unit = {
if (writer != ????) writer(sc, outSchema, dest, collection) else throw new IllegalArgumentException("Store: writeTo has been called but there is no writer function")
}
}
Upvotes: 1
Views: 314
Reputation: 5702
With @DCKing's help I think this is what I needed. No abstract class--all traits. There is a root trait that defines the attributes and one method for readFrom or writeTo. The extended trait defines the type read or written and the reader or writer. Now to extend this we just create a new read or write trait for a specific data type, only one protected function to write. Quite DRY. Using String as a standin for various types.
trait RT[T]{
val sc: String
val inSchema: String
protected def reader(sc: String, s: String, source: String): T
def readFrom(source: String): T = reader(sc, inSchema, source)
}
trait StringReadTrait extends RT[String]{
protected def reader(sc: String, s: String, source: String): String = {println("StringReadTrait#reader called, sc: "+sc+" s: "+s+" source: "+source); "StringReadTrait#reader"}
}
trait WT[T]{
val sc: String
val outSchema: String
protected def writer(sc: String, s: String, dest: String, collection: String): Unit
def writeTo(collection: String, dest: String) = writer(sc, outSchema, dest, collection)
}
trait StringWriteTrait extends WT[String]{
protected def writer(sc: String, s: String, dest: String, collection: String) = {println("writer called, sc: "+sc+" s: "+s+" dest: "+dest+" collection: "+collection)}
}
trait StringReadWriteTrait extends StringReadTrait with StringWriteTrait
class ReadStore(val inSchema: String, val sc: String) extends StringReadTrait
class WriteStore(val outSchema: String, val sc: String) extends StringWriteTrait
class ReadWriteStore(val inSchema: String, val outSchema: String, val sc: String) extends StringReadWriteTrait
Upvotes: 0
Reputation: 4283
This is a weird way to accomplish what you want. If the outSchema
is only relevant when a WriteTrait
is mixed in, and an inSchema
only when a ReadTrait
is mixed in, then why are they not part of the traits but part of some other class? If you make them part of the trait, you can have the compile ensure everything is okay instead of throwing IllegalArgumentException
s around.
trait SomeReadTrait[T] {
val inSchema: Schema
val context: SparkContext
def reader(sc: SparkContext, s: Schema, source: String): T = ???
def readFrom(source: String): T = reader(context, inSchema, source)
}
trait SomeWriteTrait[T] {
val outSchema: Schema
val context: SparkContext
def writer(sc: SparkContext, s: Schema, dest: String, collection: T) = ???
def writeTo(collection: T, dest: String) = writer(context, outSchema, dest, collection)
}
trait SomeReadWriteTrait[T] extends SomeReadTrait[T] with SomeWriteTrait[T]
class ReadStore[T](val context: SparkContext, val inSchema: Schema) extends SomeReadTrait[T]
class WriteStore[T](val context: SparkContext, val outSchema: Schema) extends SomeWriteTrait[T]
class ReadWriteStore[T](val context: SparkContext, val inSchema: Schema, val outSchema: Schema) extends SomeReadWriteTrait[T]
Isn't this what you want?
Upvotes: 3