Scala Builder pattern with phantom types - scala

Having the below builder pattern in Scala. To simplify it, I'm using 3 instances of A such that instance1 contains only field1 and has no connection to field2 or field3. The problem is that everywhere in the code I have to use val s = A.instance1.field1.get; doSomething(s), where the get call is not potentially safe. For example A.instance1.field2.get would fail on None.get. In order to guard it I have to match case against the option and deal with None cases:
object A {
val instance1 = new ABuilder().withField1("abc").build1
val instance2 = new ABuilder().withField1("abc").withField2("def").build2
val instance3 = new ABuilder().withField1("abc").withField3("def").build1
}
case class A(builder: ABuilder) {
val field1: Option[String] = builder.field1
val field2: Option[String] = builder.field2
val field3: Option[String] = builder.field3
}
class ABuilder {
var field1: Option[String] = None
var field2: Option[String] = None
var field3: Option[String] = None
def withField1(f: String): ABuilder = {
this.field1 = Some(f)
this
}
def withField2(f: String): ABuilder = {
this.field2 = Some(f)
this
}
def withField3(f: String): ABuilder = {
this.field3 = Some(f)
this
}
def build1: A = {
require(field1.isDefined, "field 1 must not be None")
A(this)
}
def build2: A = {
require(field1.isDefined, "field 1 must not be None")
require(field2.isDefined, "field 2 must not be None")
A(this)
}
}
Another solution would be to use parameterized types, also called phantom types. I found very few good tutorials on that subject, and could not find in any of them how to implement a type safe builder pattern in Scala with phantom types and actual data (or state) - all examples describe methods only.
How can I use phantom types in my example to avoid getting runtime None exceptions and get only nice type-mismatch exceptions? I'm trying to parameterize all the classes and methods mentioned and use sealed traits but had no success so far.

If you really want to use phantom types you could do
object PhantomExample {
sealed trait BaseA
class BaseAWith1 extends BaseA
final class BaseAWith12 extends BaseAWith1
object A {
val instance1 = new ABuilder().withField1("abc").build1
val instance2 = new ABuilder().withField1("abc").withField2("def").build2
}
case class A[AType <: BaseA](builder: ABuilder) {
def field1[T >: AType <: BaseAWith1] = builder.field1.get
def field2[T >: AType <: BaseAWith12] = builder.field2.get
}
class ABuilder {
var field1: Option[String] = None
var field2: Option[String] = None
def withField1(f: String): ABuilder = {
this.field1 = Some(f)
this
}
def withField2(f: String): ABuilder = {
this.field2 = Some(f)
this
}
def build1: A[BaseAWith1] = {
require(field1.isDefined, "field 1 must not be None")
A(this)
}
def build2: A[BaseAWith12] = {
require(field1.isDefined, "field 1 must not be None")
require(field2.isDefined, "field 2 must not be None")
A(this)
}
}
val x = A.instance1.field1 //> x : String = abc
val x2 = A.instance2.field1 //> x2 : String = abc
val x3 = A.instance2.field2 //> x3 : String = def
// This gives compilation error
//val x2 = A.instance1.field2
}
However, I don't recommend using this kind of code in production. I think it looks ugly, the compilation error seems cryptic, and IMHO is not the best solution. Think about it, if your instances are so different, maybe they are not even instances of the same concrete class?
trait BaseA {
def field1
}
class A1 extends BaseA { }
class A2 extends BaseA { ... def field2 = ... }

I'm not sure if this is what you want, but i think you can take it as a base.
First the A class:
case class A(field1: String = "",
field2: String = "",
field3: String = "")
The case class has default values of empty strings. This allow us to create any A object with any field value assigned without care of None values.
For example:
val b2 = A("abc", "def")
> b2: A = A(abc,def,)
val b1 = A("abc")
> b1: A = A(abc,,)
val notValidB = A(field2 = "xyz")
> notValidB: A = A(,xyz,)
As you can see b2 and b1 are valid objects, and notValidB is not valid since your object requires field1.
You can create another function that uses pattern matching to validate your A objects and then proceed with determinate actions.
def determineAObj(obj: A): Unit = obj match {
case A(f1, f2, _) if !f1.isEmpty && !f2.isEmpty => println("Is build2")
case A(f1, _, _) if !f1.isEmpty => println("Is build1")
case _ => println("This object doesn't match (build1 | build2)")
}
And then run:
determineAObj(b1)
> "Is build1"
determineAObj(b2)
> "Is build2"
determineAObj(notValidB)
> "This object doesn't match (build1 | build2)"

Related

Determine non-empty additional fields in a subclass

Assume I have a trait which looks something like this
trait MyTrait {
val x: Option[String] = None
val y: Option[String] = None
}
Post defining the trait I extend this trait to a class MyClass which looks something like this
case class MyClass(
override val x: Option[String] = None,
override val y: Option[String] = None,
z: Option[String] = None
) extends MyTrait
Now I need to find if any other property other than the properties extended by MyTrait is not None. In the sense if I need to write a method which is called getClassInfo which returns true/false based upon the values present in the case class. In this case it should return true if z is Non optional. My getClassInfo goes something like this
def getClassInfo(myClass: MyClass): Boolean = {
myClass
.productIterator
.filterNot(x => x.isInstanceOf[MyTrait])
.exists(_.isInstanceOf[Some[_]])
}
Ideally this should filter out all the fields which are not a part of Mytrait and return me z in this case.
I tried using variance, However It seems like isInstanceOf doesn't take the same
filterNot(x => x.isInstanceOf[+MyTrait])
However this cannot be possible
val a = getClassInfo(MyClass()) //Needs to return false
val b = getClassInfo(MyClass(Some("a"), Some("B"), Some("c"))) //returns true
val c = getClassInfo(MyClass(z = Some("z"))) //needs to return true
val d = getClassInfo(MyClass(x = Some("x"), y = Some("y"))) // needs to return false
The simple answer is to declare an abstract method that gives the result you want and override it in the subclass:
trait MyTrait {
def x: Option[String]
def y: Option[String]
def anyNonEmpty: Boolean = false
}
case class MyClass(x: Option[String] = None, y: Option[String] = None, z: Option[String] = None) extends MyTrait {
override def anyNonEmpty = z.nonEmpty
}
You can then call anyNonEmpty on your object to get the getClassInfo result.
Also note that I've used def here in the trait because val in a trait is generally a bad idea because of initialisation issues.
If you really need reflection you can try
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
def getClassInfo(myClass: MyClass): Boolean = {
def fields[A: TypeTag] = typeOf[A].members.collect {
case m: MethodSymbol if m.isGetter && m.isPublic => m
}
val mtFields = fields[MyTrait]
val mcFields = fields[MyClass]
val mtFieldNames = mtFields.map(_.name).toSet
val mcNotMtFields = mcFields.filterNot(f => mtFieldNames.contains(f.name))
val instanceMirror = currentMirror.reflect(myClass)
val mcNotMtFieldValues = mcNotMtFields.map(f => instanceMirror.reflectField(f).get)
mcNotMtFieldValues.exists(_.isInstanceOf[Some[_]])
}
val a = getClassInfo(MyClass()) //false
val b = getClassInfo(MyClass(Some("a"), Some("B"), Some("c"))) //true
val c = getClassInfo(MyClass(z = Some("z"))) //true
val d = getClassInfo(MyClass(x = Some("x"), y = Some("y")))//false

In Scala, how to deal with heterogeneous list of the same parameterized type

I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)
object Row {
val buffer : Array[Any] = Array(42, 21, true)
}
And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.
object GenericsOp {
import Row._
val ops = Seq(Count, Exist)
def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}
By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is
Type mismatch, expected: _$1, actual: AnyVal
Is there a way to make it work ?
I've found a workaround by using abstract type member instead of type parameter.
object AbstractOp extends App {
import Row._
trait Op {
type A
def compute(a: A) : A
}
case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}
val ops = Seq(Count, Exist)
def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}
Is there a better way ?
It seems that your code can be simplified by making Op[A] extend Any => A:
trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}
Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.
Update
If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}
Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.
As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:
def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))
buffer(0) = apply(ops(0), buffer(0))

Scala: How to pattern match scala.Long and java.lang.Long

I need to do a pattern match on Classes. The Problem is, that I have some Problems matching a Long.
I need to handle scala.Long and java.lang.Long in the same way, but why do I need to declare them both in cases?
Here is an example:
def test(typ: Class[_]) {
typ match {
case q if q == classOf[Long] => println("scala long...")
}
}
val scalaLongField: java.reflect.Field = ......
val javaLongField: java.reflect.Field = ......
test(scalaLongField.getType) // prints "scala long..."
test(javaLongField.getType) // scala.MatchError: class java.lang.Long (of class java.lang.Class)
Is there a way to handle them the same without having an instance but just the class?
The reason is that java.lang.Long and Long are different classes. In Java, there is a difference between java.lang.Long.class and Long.TYPE. Similarly, in Scala, classOf[Long] and classOf[java.lang.Long] are different.
If you want to pattern match on classes, you can create helper unapply methods for that:
object ScalaLong {
// Internal helper:
private def matchClass[T](c: Class[_], as: Class[T]): Option[Class[T]] =
if (as.isAssignableFrom(c)) Some(as)
else None;
// Matches wrapped Long classes.
object LongObject {
def unapply(c: Class[_]): Option[Class[java.lang.Long]] =
matchClass(c, classOf[java.lang.Long]);
}
// Matches primitive long classes.
object LongPrim {
def unapply(c: Class[_]): Option[Class[Long]] =
matchClass(c, classOf[Long]);
}
// -- Test:
def check(clz: Class[_]) =
clz match {
case LongPrim(c) => println("Long primitive: " + c);
case LongObject(c) => println("Long object: " + c);
case _ => println("Other: " + clz);
}
class Example {
val l1: scala.Long = 1L;
val l2: java.lang.Long = 1L;
val l3: java.lang.Integer = 1;
}
def main(argv: Array[String]) {
for(name <- Seq("l1", "l2", "l3"))
check(classOf[Example].getMethod(name).getReturnType());
}
}
Generally, you'll have to treat classOf[Long] and classOf[java.lang.Long] separately. Perhaps, if you describe what you need to do with them, we can find a better solution for your specific task.
It should work straight away:
object LongTest {
def test(value: Any): Boolean = value match {
case l: Long => true
case _ => false
}
def run() {
println(test(1L))
println(test(new java.lang.Long(1L)))
}
}
LongTest.run() // true and true
It wasn't obvious to me that you want to match classes instead of instance. I'm not sure I understand what you actually want. Like this?
object LongTest {
def test(clazz: Class[_]): Boolean =
clazz == classOf[Long] || clazz == classOf[java.lang.Long]
def run() {
println(test(1L.getClass))
println(test(new java.lang.Long(1L).getClass))
}
}
LongTest.run() // true and true
Or as a pattern match:
def test(clazz: Class[_]): Boolean = clazz match {
case q if q == classOf[Long] || q == classOf[java.lang.Long] => true
case _ => false
}

How to use extractor in polymorphic unapply?

I don't really get this little thingy. I have an abstract class Box
with several sub-classes for different types. For example
abstract class Box
class StringBox(val sValue : String) extends Box
The apply method in the companion object for Box is simple:
object Box{
def apply(s: String) = new StringBox(s)
def apply(b: Boolean) = new BooleanBox(b)
def apply(d: Double) = new DoubleBox(d)
}
so I can write
val sb = Box("StringBox)
Okay, writing unapply makes some trouble. My first idea was to use pattern matching on the type, like this this:
def unapply(b: Box) = b match {
case sb: StringBox => Some(sb.sValue)
case bb: BooleanBox => Some(bb.bValue)
case db: DoubleBox => Some(db.dValue)
case _ => None
}
Which simply doesn't work because of type erasures.
Second attempt was a generic Box[T] with type T and an abstract type member re-defined
in each sub classes. For instance:
abstract class Box[T] {def value : T}
class StringBox(val sValue : String) extends Box[String] {
override def value : String = sValue
}
Consequently, I can re write my unapply as:
def unapply[T](b: Box[T]) = b match {
case sb: Box[String] => Some(sb.value)
case bb: Box[Boolean] => Some(bb.value)
case db: Box[Double] => Some(db.value)
case _ => None
Unfortunately, this doesn't work either. So I guess the explicit type reference
in Box[String] gets erased as well so I need to use a type manifest instead.
Maybe something like:
def unapply[T](b: Box[_])(implicit target: Manifest[T]): Option[T] = {
if(b.value == target) Some(b.value.asInstanceOf[T])
else None
}
This code compiles (2.10) but still does not the desired implicit conversion.
Why?
Simple question, is there a way to do value extraction without using reflection
or a manifest?
What really boggles me is the question if there is a simple(r) way to combine
polymorphism and pattern matching? If not, are there other ways in Scala to
accomplish a similar effect?
Any idea or suggestions?
Thank you very much.
Prolly you can try this.. :)
abstract class Box[T](val v: T)
object Box {
def apply(s: String) = new StringBox(s)
def apply(b: Boolean) = new BooleanBox(b)
def apply(d: Double) = new DoubleBox(d)
}
class StringBox(sValue: String) extends Box(sValue)
object StringBox {
def unapply(b: StringBox) = Some(b.v)
}
class BooleanBox(sValue: Boolean) extends Box(sValue)
object BooleanBox {
def unapply(b: BooleanBox) = Some(b.v)
}
class DoubleBox(sValue: Double) extends Box(sValue)
object DoubleBox {
def unapply(b: DoubleBox) = Some(b.v)
}
You can use it as --
def useCase[T](box: Box[T]) = box match {
case StringBox("StringBoxxx") => "I found the StringBox!"
case StringBox(s) => "Some other StringBox"
case BooleanBox(b) => {
if (b) "Omg! its true BooleanBox !"
else "its false BooleanBox :("
}
case DoubleBox(x) => {
if (x > 3.14) "DoubleBox greater than pie !"
else if (x == 3.14) "DoubleBox with a pie !"
else "DoubleBox less than a pie !"
}
case _ => "What is it yaa ?"
}
useCase(Box("StringBoxxx")) //> res0: String = I found the StringBox!
useCase(Box("Whatever !")) //> res1: String = Some other StringBox
useCase(Box(true)) //> res2: String = Omg! its true BooleanBox !
useCase(Box(false)) //> res3: String = its false BooleanBox :(
useCase(Box(4)) //> res4: String = DoubleBox greater than pie !
useCase(Box(3.14)) //> res5: String = DoubleBox with a pie !
useCase(Box(2)) //> res6: String = DoubleBox less than a pie !

Merge two case class of same type, except some fields

If you have a case class like:
case class Foo(x: String, y: String, z: String)
And you have two instances like:
Foo("x1","y1","z1")
Foo("x2","y2","z2")
Is it possible to merge instance 1 in instance 2, except for field z, so that the result would be:
Foo("x1","y1","z2")
My usecase is just that I give JSON objects to a Backbone app through a Scala API, and the Backbone app gives me back a JSON of the same structure so that I can save/update it. These JSON objects are parsed as case class for easy Scala manipulation. But some fields should never be updated by the client side (like creationDate). For now I'm doing a manual merge but I'd like a more generic solution, a bit like an enhanced copy function.
What I'd like is something like this:
instanceFromDB.updateWith(instanceFromBackbone, excludeFields = "creationDate" )
But I'd like it to be typesafe :)
Edit:
My case class have a lot more fields and I'd like the default bevavior to merge fields unless I explicitly say to not merge them.
What you want is already there; you just need to approach the problem the other way.
case class Bar(x: String, y: String)
val b1 = Bar("old", "tired")
val b2 = Bar("new", "fresh")
If you want everything in b2 not specifically mentioned, you should copy from b2; anything from b1 you want to keep you can mention explicitly:
def keepY(b1: Bar, b2: Bar) = b2.copy(y = b1.y)
scala> keepY(b1, b2)
res1: Bar = Bar(new,tired)
As long as you are copying between two instances of the same case class, and the fields are immutable like they are by default, this will do what you want.
case class Foo(x: String, y: String, z: String)
Foo("old_x", "old_y", "old_z")
// res0: Foo = Foo(old_x,old_y,old_z)
Foo("new_x", "new_y", "new_z")
// res1: Foo = Foo(new_x,new_y,new_z)
// use copy() ...
res0.copy(res1.x, res1.y)
// res2: Foo = Foo(new_x,new_y,old_z)
// ... with by-name parameters
res0.copy(y = res1.y)
// res3: Foo = Foo(old_x,new_y,old_z)
You can exclude class params from automatic copying by the copy method by currying:
case class Person(name: String, age: Int)(val create: Long, val id: Int)
This makes it clear which are ordinary value fields which the client sets and which are special fields. You can't accidentally forget to supply a special field.
For the use case of taking the value fields from one instance and the special fields from another, by reflectively invoking copy with either default args or the special members of the original:
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
import System.{ currentTimeMillis => now }
case class Person(name: String, age: Int = 18)(val create: Long = now, val id: Int = Person.nextId) {
require(name != null)
require(age >= 18)
}
object Person {
private val ns = new java.util.concurrent.atomic.AtomicInteger
def nextId = ns.getAndIncrement()
}
object Test extends App {
/** Copy of value with non-defaulting args from model. */
implicit class Copier[A: ClassTag : TypeTag](val value: A) {
def copyFrom(model: A): A = {
val valueMirror = cm reflect value
val modelMirror = cm reflect model
val name = "copy"
val copy = (typeOf[A] member TermName(name)).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = typeOf[A] member TermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(valueMirror reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
val pmethod = typeOf[A] member p.name
if (pmethod != NoSymbol) (modelMirror reflectMethod pmethod.asMethod)()
else throw new RuntimeException("No $p on model")
}
}
val args = (for (ps <- copy.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(valueMirror reflectMethod copy)(args: _*).asInstanceOf[A]
}
}
val customer = Person("Bob")()
val updated = Person("Bobby", 37)(id = -1)
val merged = updated.copyFrom(customer)
assert(merged.create == customer.create)
assert(merged.id == customer.id)
}
case class Foo(x: String, y: String, z: String)
val foo1 = Foo("x1", "y1", "z1")
val foo2 = Foo("x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("x1", "y1", "z2")
If you change Foo later to:
case class Foo(w: String, x: String, y: String, z: String)
No modification will have to be done. Explicitly:
val foo1 = Foo("w1", "x1", "y1", "z1")
val foo2 = Foo("w2", "x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("w1", "x1", "y1", "z2")