Factory design pattern in Scala with case classes - scala

I'm trying to implement a factory design pattern in Scala using the apply methods available on the companion object. I have the following approach.
sealed trait MyType {
def param: String
}
case class TypeA(param: String) extends MyType
case class TypeB(param: String, anotherParam: String) extends MyType
object MyType {
def apply(param: String): TypeA = ???
def apply(param, anotherParam: String): TypeB = ???
}
How do I now force the callers of the above trait to go via the companion object when creating instances of TypeA or TypeB?

You can move the case classes inside the companion object, and set the constructors to be private and accessed only within the companion object.
sealed trait MyType {
def param: String
}
object MyType {
case class TypeA private[MyType] (param: String) extends MyType
case class TypeB private[MyType] (param: String, anotherParam: String) extends MyType
def apply(param: String): TypeA = TypeA(param)
def apply(param: String, anotherParam: String): TypeB = TypeB(param, anotherParam)
}
No one would be able to instantiate the case classes directly, unless though reflection.
scala> MyType("Test")
res0: MyType.TypeA = TypeA(Test)
scala> MyType("Test", "another test")
res1: MyType.TypeB = TypeB(Test,another test)
scala> MyType.TypeA("test??")
<console>:12: error: constructor TypeA in class TypeA cannot be accessed in object $iw
MyType.TypeA("test??")
^

You can simply call the apply method of the case classes themselves. There doesn't seem to be a way to prevent client code from calling TypeA.apply directly, though, as that would prevent MyType from calling it.
sealed trait MyType {
def param: String
}
case class TypeA(param: String) extends MyType
case class TypeB(param: String, anotherParam: String) extends MyType
object MyType {
def apply(param: String): TypeA = TypeA(param)
def apply(param: String, anotherParam: String): TypeB = TypeB(param, anotherParam)
}

The trait MyType is sealed. That me others can do something like new MyType{} to instantiate it.
Then you can remove the case classes.
// No more public case classes TypeA & TypeB
object MyType {
def apply(p: String): MyType = /* case A */ new MyType { val param = p }
private case class InternalB(param: String, other: String) extends MyType
def apply(param: String, anotherParam: String): MyType = InternalB(param, anotherParam)
}
At this point, it's required to use companion object to create MyType instances.
Then you can restore pattern matching for these different cases.
object MyType {
// the apply functions, plus extractors thereafter...
/** Extracts mandatory parameter whatever is the case. */
def unapply(t: MyType): Option[String] = Some(t.param)
/** Extracts both parameter, extra parameter for case B, None for other */
def unapply(t: MyType): Option[(String, String)] = t match {
case InternalB(mandatory, extra)/* Only possible there as private */ =>
Some(mandatory -> extra)
case _ => None
}
}
// Then pattern matching can do...
val test1: Boolean = MyType("A") match {
case MyType(param) => true
case _ => false
}
// Will be true
val test2: Boolean = MyType("B", "extraB") match {
case MyType(param, extra) => true
case _ => false
}
// Will be true
val test3: Int = MyType("A") match {
case MyType(param, extra) => 2
case MyType(param) => 1
case _ => 0
}
// Will be 1
val test4: Boolean = MyType("B", "extraB") match {
case MyType(param) => true
case _ => false
}
// Will be true
It allows a full control over instantiation, and abstraction over implementation of cases.

Related

Type Parameter in Case Class Using Trait w/ Implicits

Let's say we have a trait:
trait ThingToThing[-A, +B] { def apply(a: A): B }
and its companion object:
object ThingToThing {
implicit object StringToBoolean extends ThingToThing[String, Boolean] {
override def apply(a: String): Boolean = a.toBoolean
}
}
and a case class:
case class Thing[A](a: A) {
def to[B](implicit thing: ThingToThing[A, B]): B = thing(a)
}
This allows me to do the following:
Thing("true").to[Boolean]
res0: Boolean = true
This is all fine and dandy, and I can do something like:
case class MyClass(ss: Seq[String]) {
def doStuff(s: String) = Thing(s).to[Boolean]
}
But what I'd like to do, however, is something like:
case class MyClass[B](ss: Seq[String]) {
def doStuff(s: String) = Thing(s).to[B]
}
But, this errors with:
error: could not find implicit value for parameter thing: ThingToThing[String,B]
Is there a way I can use a type parameter in my MyClass?
** Don't get caught up on the toy example of converting a String to a Boolean; I just used this as a simple example to illustrate the problem.
The compiler couldn't find an implicit instance of ThingToThing[String,B] (B is unknown) in the call site Thing(s).to[B]:
case class MyClass[B](ss: Seq[String]) {
def doStuff(s: String) = Thing(s).to[B]
}
thus the error.
You can declare the required implicit in the constructor to have it resolved in the call site of object creation (when B is known):
case class MyClass[B](ss: Seq[String])(implicit t2t: ThingToThing[String, B]) {
def doStuff(s: String) = Thing(s).to[B]
}
, or declare it in the method to have it resolved in the call site of the method invocation (when B is known):
case class MyClass[B](ss: Seq[String]) {
def doStuff(s: String)(implicit t2t: ThingToThing[String, B]) = Thing(s).to[B]
}

Scala case class with multiple-type argument

I need to check integrity of nested schemas and hence am writing case classes to do so. The main hurdle I am facing is the schema may have a field (say, name) either of a String or a Utf8 type and I want to accept both the instances. Is it possible to avoid having two case classes as
case class NameValueString(name: String, value: Double)
case class NameValueUtf8(name: Utf8, value: Double)
and something like
case class NameValue(name #(_:String | _:Utf8), value: Double)
The above expression certainly fails compilation.
Nikhil
One approach is so-called type classes:
trait StringLike[A] // sealed if you don't want anybody to implement it elsewhere
object StringLike {
implicit object StringEv extends StringLike[String] {}
implicit object Utf8Ev extends StringLike[Utf8] {}
}
case class NameValue[A](name: A, value: Double)(implicit val stringLike: StringLike[A])
Of course, StringLike will normally not be empty, but describe whatever common functionality you need from both String and Utf8.
You can match on the evidence:
def nameLength[A](nameValue: NameValue[A]) = nameValue.stringLike match {
case StringLike.StringEv =>
nameValue.name.length // calls String#length
case StringLike.Utf8Ev =>
nameValue.name.length // calls Utf8#length (assuming Utf8 has such method)
}
In this case the compiler will even know that A (and so the type of nameValue.name) is String in the first branch and Utf8 in the second.
Another pattern (doesn't require implicit arguments):
import scala.language.implicitConversions
class StringLike[A](name: A) {
override def toString = {
name match {
case s: String => s"String: $s"
case i: Int => s"Int: $i"
}
}
}
implicit def string2StringLike(s: String) = new StringLike(s)
implicit def int2StringLike(i: Int) = new StringLike(i)
case class NameValue[A](name: StringLike[A], value: String) {
override def toString = name.toString
}
NameValue("123", "123")
//> NameValue[String] = String: 123
NameValue(13, "123")
//> NameValue[Int] = Int: 13
NameValue(13.9, "123")
// error: type mismatch;
// found : Double(13.9)
// required: StringLike[?]
// NameValue(13.9, "123")
// ^
UPDATE
Here's how I see completed type class approach based on Alexey's answer:
trait StringLike[A] {
def toString(x: A): String
}
object StringLike {
implicit object StringStringLike extends StringLike[String] {
def toString(s: String) = s"String: $s"
}
implicit object IntStringLike extends StringLike[Int] {
def toString(i: Int) = s"Int: $i"
}
}
import StringLike._
case class NameValue[A](name: A, value: Double)(implicit ev: StringLike[A]) {
override def toString = ev.toString(name)
}
NameValue(1, 2.0)
//> NameValue[Int] = Int: 1
NameValue("123", 2.0)
//> NameValue[String] = String: 123
NameValue(2.0, 2.0)
// error: could not find implicit value for parameter ev:
// StringLike[Double]
// NameValue(2.0, 2.0)
// ^
UPDATE2
One more (using union type for type safety):
type ¬[A] = A => Nothing
type ¬¬[A] = ¬[¬[A]]
type ∨[T, U] = ¬[¬[T] with ¬[U]]
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }
def nameLength[A: ClassTag: (Int |∨| String)#λ](nameValue: NameValue[A]) =
nameValue.name match {
case s:String => s.length
case i:Int => i + 1
}
As you are using case class already, if you just need different ways to create it, and you are ok on keeping your data represented in only one way, you can add your own apply method to enable the creation using different parameters.
case class NameValue(name: String, value: Double)
object NameValue{
def apply(name: Utf8, value: Double): NameValue = {
new NameValue( name.toString, value )
}
}
Alternatively, if you would like to pattern match and extract NameValue from different options, you may need to check Extractors, which is basically create your own unapply methods... check http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

Scala implicit string conversions utility

I'm trying to write some string utils to be able to do implicit conversions of the form "myString".doSpecialConversion and have a specialValue based on which doSpecialConversion works.
Attempt-1: Use a trait:
trait ConversionUtils {
// Need an overridable value
lazy val specialValue = ","
implicit class StringConversions(val s: String) {
def doSpecialConversion: Option[String] = if (s == specialValue) None else Some(s)
}
}
Trait works just fine but the problem is that its not static so multiple StringConversions will be created which is undesired. So I try to extend AnyVal which can't be done for a trait because Another limitation that is a result of supporting only one parameter to a class is that a value class must be top-level or a member of a statically accessible object.
Attempt-2: Use a singleton:
object ConversionUtils {
// Need an overridable value
lazy val specialValue = ","
implicit class StringConversions(val s: String) extends AnyVal {
def doSpecialConversion: Option[String] = if (s == specialValue) None else Some(s)
}
}
Question: How do I provide a Util to be able to override the specialValue for StringConversions and be true-ly static?
You can ask for an implicit parameter:
object ConversionUtils {
case class SpecialValue(str: String)
implicit class StringConversions(val s: String) extends AnyVal {
def doSpecialConversion(implicit sv: SpecialValue): Option[String] = if (s == sv.str) None else Some(s)
}
}
Usage:
scala> implicit val sp = SpecialValue(",")
sp: ConversionUtils.SpecialValue = SpecialValue(,)
scala> "aaa".doSpecialConversion
res0: Option[String] = Some(aaa)
scala> ",".doSpecialConversion
res1: Option[String] = None
In general case, macro-libraries, like machinist might help to get rid of boilerplate.

Introduce type constraint for compile time safety

If we have a type class with some ADT as follow
trait MyRule {
type T
class ResourceIdType[A](val value: A)
case class StringResourceIdType(override val value: String) extends ResourceIdType(value)
case class ListResourceIdType(override val value: List[Int]) extends ResourceIdType(value)
def resourceType(rtm: T) : Any
private foo(rtm: T ) = {
resourceType(rtm) match{
case StringResourceIdType(s: String) =>
case ListResourceIdType(l:List[Int]) =>
...
}
type class impls override def resourceType as follow
object FirstClient{
implicit val clientRule = new MyRule{
type T = SomeType
def resourceType(rtm: T) = StringResourceIdType(rtm.name) //assume SomeType has a name property of type String
}
}
object SecondClient{
implicit val clientRule2 = New MyRule{
type T = SomeType2
def resourceType(rtm: T) = ListResourceIdType(rtm.ids) //assume SomeType2 has a ids property of type List[Int]
}
}
Want to make def resourceType(rtm: T) : Any compile time type safe/checked by removing Any and replace with a valid type as T <: ResourceIdType. What would be the right way to address this?
You can add a type parameter in MyRule trait and refine it in each type classes:
trait MyRule {
type T
class ResourceIdType[A](val value: A)
case class StringResourceIdType(override val value: String) extends ResourceIdType(value)
case class ListResourceIdType(override val value: List[Int]) extends ResourceIdType(value)
type F <: ResourceIdType[_]
def resourceType(rtm: T) : F
private def foo(rtm: T): Unit = {
resourceType(rtm) match {
case StringResourceIdType(s: String) =>
case ListResourceIdType(l:List[Int]) =>
}
}
}
object FirstClient{
implicit val clientRule = new MyRule{
case class SomeType(name: String)
type T = SomeType
type F = StringResourceIdType
def resourceType(rtm: T) = StringResourceIdType(rtm.name) //assume SomeType has a name property of type String
}
}
object SecondClient{
implicit val clientRule2 = new MyRule{
case class SomeType2(ids: List[Int])
type T = SomeType2
type F = ListResourceIdType
def resourceType(rtm: T) = ListResourceIdType(rtm.ids) //assume SomeType2 has a ids property of type List[Int]
}
}
The following compiles. Please let me know if it addresses your problem.
package net
trait SomeType {
def name: String
}
trait SomeType2 {
def ids: List[Int]
}
// Introduce an Algebraic Data Type
class ResourceValue
case class StringValue(x: String) extends ResourceValue
case class ListIntsValue(x: List[Int]) extends ResourceValue
trait MyRule {
type T
sealed trait ResourceIdType {
val value: ResourceValue
}
case class StringResourceIdType(override val value: StringValue) extends ResourceIdType
case class ListResourceIdType(override val value: ListIntsValue) extends ResourceIdType
def resourceType(rtm: T): ResourceIdType
private def foo(rtm: T): ResourceValue = {
resourceType(rtm) match {
case StringResourceIdType(s # StringValue(_)) => s
case ListResourceIdType(l # ListIntsValue(_)) => l
}
}
}
object FirstClient{
implicit val clientRule = new MyRule {
type T = SomeType
override def resourceType(rtm: T): ResourceIdType =
StringResourceIdType(StringValue(rtm.name))
}
}
object SecondClient{
implicit val clientRule2 = new MyRule {
type T = SomeType2
override def resourceType(rtm: T): ResourceIdType =
ListResourceIdType(ListIntsValue(rtm.ids))
}
}

How to extend an abstract class in scala and use the abstract constructor

I have abstract class A
abstract class A{
def this(obj:Object){
this()
obj match{
case s:String => stringMethod(s)
case n:Int => intMethod(n)
}
def stringMethod(s:String)
def intMethod(n:Int)
}
and I have a class that extends this class
class B(obj:Object) extends A(obj){
var s:String = null
def stringMethod(s:String){
this.s = s
}
def intMethod(n:Int){
this.s = n.toString
}
}
The point of this class is to instantiate an object and its variables depending on the class type of the object used to instantiate it but the problem is that when the abstract constructor is called, the default constructor of object which is extending the abstract object is somehow being called after. This alters the value of var s back to null.
This a really simple implementation of my classes and I have more variables in class B and more logic inside intMethod and stringMethod.
I realize this might be a completely wrong way of doing this, so if there is a better way please let me know.
The body of a super class is always executed before the body of a sub class. In your case A calls stringMethod or intMethod first, then finally B's body is executed, assign null to s. If you remove that assignment, it should work:
abstract class A{
def this(obj:Object){
this()
obj match{
case s:String => stringMethod(s)
case n:Int => intMethod(n)
}
}
def stringMethod(s:String)
def intMethod(n:Int)
}
class B(obj:Object) extends A(obj){
var s:String = _ // !
def stringMethod(s:String){
this.s = s
}
def intMethod(n:Int){
this.s = n.toString
}
}
val b = new B("foo")
b.s
Neverless, the style is problematic. Here are two alternatives:
trait A {
def obj: Any
def s: String
}
class B(val obj: Any) extends A {
val s = obj match {
case i: Int => i.toString
case x: String => x
case x => throw new IllegalArgumentException(x.toString)
}
}
Or better statically checked:
object B {
def apply(i: Int ): B = new B(i, i.toString)
def apply(s: String): B = new B(s, s)
}
class B private(val obj: Any, val s: String) extends A
B(1.0) // not allowed