Generic sealed trait fromString() method - scala

Considering the following code :
sealed trait Foo {
def name: String
}
case object FooA extends Foo {
override val name: String = "a"
}
case object FooB extends Foo {
override val name: String = "b"
}
object Foo {
def fromString(name: String): Foo = {
name match {
case FooA.name => FooA
case FooB.name => FooB
}
}
Can I refactor the fromString() method to avoid having a case per case object instance ? Some more generic code able to enumerate through all Foo instances ?
In my real version, I start to have a lot of case object, and the wall of case bla.name => bla is boring me ^^
Thanks :)

Beachape provide an enum library which can do this for you out of the box:
include this in your build.sbt
"com.beachape" %% "enumeratum" % "1.5.15"
The Enum class provided just needs to be extended like so:
import enumeratum._
sealed trait Foo
object Foo extends Enum[Foo] {
case object FooA extends Foo
case object FooB extends Foo
}
There is a function called withName that will allow you to get the right sealed trait via its string name:
Foo.withName("FooA")

How about something like this?
sealed trait Foo {
def name: String
}
object Foo {
case object FooA extends Foo {
override val name: String = "a"
}
case object FooB extends Foo {
override val name: String = "b"
}
val values = Seq(FooA, FooB)
def withName(name: String): Option[Foo] = {
values.find(value => value.name.equals(name))
}
}
You can then use the withName(String) method to get the corresponding concrete object of type Foo as an Option:
val testFooAName = "a"
val testFooA = Foo.withName(testFooAName) // Will yield Some(FooA)
testFooA match {
case Some(Foo.FooA) => println("Matched Foo.FooA!")
case Some(Foo.FooB) => println("Matched Foo.FooB!")
}
val testFooNoneName = "none"
val testFooNone = Foo.withName(testFooNoneName) // Will yield None
Output:
Matched Foo.FooA!

Related

Scala sealed trait - replicate Enumeration `withName` method

I'm trying to refactor an Enumeration to a sealed trait with concrete classes because I need to pack more functionality into them. With the sealed trait, I'd like to have functionality similar to Enumeration's withName(String) method. I've come up with the following code to do so and am looking for feedback:
sealed trait Foo {
def name: String
}
object Foo {
case object FooA extends Foo {
override val name: String = "a"
}
case object FooB extends Foo {
override val name: String = "b"
}
val values = Seq(FooA, FooB)
def withName(name: String): Option[Foo] = {
values.find(value => value.name.equals(name))
}
}
I can then use the withName(String) method to get the corresponding concrete object of type Foo as an Option:
val testFooAName = "a"
val testFooA = Foo.withName(testFooAName) // Will yield Some(FooA)
testFooA match {
case Some(Foo.FooA) => println("Matched Foo.FooA!")
case Some(Foo.FooB) => print("Matched Foo.FooB!")
}
val testFooNoneName = "none"
val testFooNone = Foo.withName(testFooNoneName) // Will yield None
Output:
Matched Foo.FooA!
Is this approach correct?
Yes, it looks fine
Minor simplification: Have a map for fast lookup
val values = Seq(FooA, FooB)
val fastLookup = values.map(v => v.name -> v).toMap
def withName(name: String): Option[Foo] = fastLookup.get(name)
There is a handy library by beachape which can be used to provide some of the functionality of enums to your sealed trait
the package you need to include in your sbt is:
"com.beachape" %% "enumeratum" % "1.5.15"
then extend your your object with the Enum like so:
import enumeratum._
sealed trait Foo
object Foo extends Enum[Foo] {
case object FooA extends Foo
case object FooB extends Foo
}
The you can use the withName function (amongst others) to get the sealed trait you want:
Foo.withName("FooA")

how to get enums value using case classes in scala

I am trying to implement enum using case class in scala.
with reference to question type parameter in traits to define data types in scala
sealed trait ElementType[+A] {
def length: Int
}
sealed trait Tag extends ElementType[Int] {
override def length: Int = 0
}
case object Tag100 extends Tag
sealed trait Value[+A] extends ElementType[A] {
override def length: Int = 0
}
final case class Value100(a: String) extends Value[String] {
override def length: Int = a.length
}
case class MessageId(tag: Tag, value: Value[String]){
}
case class MessageRepType(tag: Tag[Int], value:MessageType ){
}
sealed trait Message[T] {def typeCode: T;}
object MessageTypes {
sealed trait MessageType[Int] extends Message[Int]
case object TYPE1 extends MessageType[Int]{val typeCode = 0;}
case object TYPE2 extends MessageType[Int]{val typeCode = 1;}
}
case class ABCMessage (
id:MessageId,
messageType:MessageRepType)
I want to print a message with something like below id and type in new line
type value
100 abc
200 0
Explanation: 100 abc represents MessageId
200 0 represnets MessageRepType and O represent TYPE1
Assuming your "message" is a ABCMessage(MessageId(Tag100, Value100("abc"),MessageRepType(Tag200, TYPE1)) (also assuming Tag200 is similar to Tag100 and case class MessageRepType(tag: Tag[Int], value:MessageType) is case class MessageRepType(tag: Tag, value:MessageType) and sealed trait MessageType extends Message[Int]):
val sep = System.lineSeparator
def convertTag(tag: Tag): Int = tag match {
case Tag100 => 100
case Tag200 => 200
}
def convertMessageId(message: MessageId): String = {
val typ = convertTag(message.tag)
val value = message.value match {
case Value100(v) => v
}
s"$typ\t$value"
}
val id = convertMessageId(message.id)
def convertType(message: MessgeRepType): String = {
val typ = convertTag(message.tag)
val value = message.value match {// I would put typeCode to MessageType, but it is not there, so pattern matching
case TYPE1 => TYPE1.typeCode
case TYPE2 => TYPE2.typeCode
}
s"$typ\t$value"
}
val typ = convertType(message)
val message: ABCMessage = ABCMessage(MessageId(Tag100, Value100("abc"),MessageRepType(Tag200, TYPE1))
val messageAsString = Seq("type\tvalue", id, typ).mkString(sep)
println(messageAsString)
A different approach as still not sure what you want to achieve. The other answer with pattern matching is what I would prefer, more idiomatic.
You can use something like a simplified the visitor pattern (removed the irrelevant parts):
trait Show {
def show: String
}
sealed trait Tag extends Show {
override def show: String = this.getClass.getSimpleName.init
}
//object Tag {
case object Tag100 extends Tag
case object Tag200 extends Tag
//}
This way you can show the tags like this:
Tag100.show // Tag100
The case classes can also implement show:
sealed trait Value[+A] extends Show {
def v: String
override def show: String = v
}
case class Value100(v: String) extends Value[String]
You can use this in other methods too to implement the printing you want:
def convertMessageId(message: MessageId): String = {
val typ = message.tag.show
val value = message.value.show
s"$typ\t$value"
}
val id = convertMessageId(message.id)
def convertType(message: MessgeRepType): String = {
val typ = message.tag.show
val value = message.value.show
s"$typ\t$value"
}
val message: ABCMessage = ABCMessage(MessageId(Tag100, Value100("abc"),MessageRepType(Tag200, TYPE1))
val messageAsString = Seq("type\tvalue", id, typ).mkString(sep)
println(messageAsString)
If type safety and easy, generic usage is also important, take a look at Shapeless' offerings and this blog post.
If type safety is not so important, maybe for your use case it is enough to know that case classes implement Product, so you can visit them one-by-one and use its content.
Alternative solution might be to convert your datastructure to for example JSON (there are many Scala libs for that) and use that for further processing. Still generic, but not type-safe.
(This question might also be relevant.)

Scala - object extends abstract class and takes parameters

//File Animal.scala
abstract class Animal {
val name: String
def getSomething(tClass: TypeClass): String = {
tClass.tName.split('.').lift(0)
}
def apply(tClass: TypeClass): SomeOtherClassType = {
// something...
}
// File: DogSpike, this is used for some specific cases (overwrites
base class val)
object DogSpike extends Animal {
override val name: String = "Spike"
}
this call then works (calls apply)
myTransformation(() => DogSpike(this))
Now I would like to create a more generic object that one can pass arguments but I am unable to.
It would work to create a derived Object from Animal that takes one arguments and being able to use the apply call
object TheDog(name: String) extends Animal {
override val name: String = "Spike"
//...
}
not sure how to implicitly call Animal.apply for TheDog object where I could pass a parameter (name)
myTransformation(() => TheDog(this))
// also this seems to be incorrect "*Wrong top statement declaration*"
object TheDog(val n: String) extends Animal {
override val name: String = n
//...
}
As of *Wrong top statement declaration* (I can understand only this part of your question) - you can't have constructor in object as object is a singleton, so you should use a case class (ADT):
final case class TheDog(name: String) extends Animal
scala>TheDog("Spike")
res2_2: TheDog = TheDog("Spike")
val and companion object with apply is added automatically for case classes, so you don't need to define your own own apply in Animal. case class TheDog(val name: String) is same as case class TheDog(name: String).
I's also use traits instead of abstract class:
trait Animal {
val name: String
def getSomething: String = {
"Dog: " + name
}
}
I don't understand your TypeClass type, but if you really want type classes:
trait Animal {
def name: String
}
final case class TheDog(name: String) extends Animal
final case class TheCat(name: String) extends Animal
implicit class RichDog(dog: TheDog){
def getSomething: String = {
"Dog" + dog.name
}
}
implicit class RichCat(cat: TheCat){
def getSomething: String = {
"Cat: " + cat.name
}
}
scala> TheDog("Spike").getSomething
res4_5: String = "DogSpike"
scala> TheCat("Tom").getSomething
res4_6: String = "Cat: Tom"
About calling apply "implicitly", I don't know why would anyone need this, but:
trait AnimalFactory[A <: Animal] {
def apply(name: String)(implicit constructor: String => A) = constructor(name)
}
object TheeeDog extends AnimalFactory[TheDog]
implicit def createDog(name: String) = TheDog(name)
TheeeDog("Spike")
Of course you have to provide createDog and make it visible for a client, but it doesn't really make sense if you can just use ADTs and define additional required applys in companion object:
case class TheMouse(name: String)
object TheMouse{
def apply(isJerry: Boolean): TheMouse = if (isJerry) TheMouse("Jerry") else TheMouse("NotJerry")
}
TheMouse(true)
If you want to add some parameter to constructor, just add it:
class AnimalFactory(clazz: SomeClass){
def doSomething = clazz.name
def apply(name: String)
}
val dogFactory = new AnimalFactory(dogClassDescriptor)
val catFactory = new AnimalFactory(catClassDescriptor)
dogFactory("Spike")
catFactory("Tom")
You can even create a factory for factory (I wouldn't recommend - this solution already looks overcomplicated):
object AnimalFactory{ //please don't use classes for that - avoiding `new` is not their purpose
def apply(clazz: SomeClass) = new AnimalFactory(clazz)
}
val dogFactory = AnimalFactory(dogClassDescriptor)
//or even `val spike = AnimalFactory(dogClassDescriptor)("Spike")`
But still what's the point if you could just provide underlying clazz either as a member or just in a wrapper:
final case class ClazzWrapper[T <: Animal](clazz: SomeClass, animal: T)

Factory design pattern in Scala with case classes

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.

How do I create an enum in scala that has an extra field

In Java I have something like this
public enum FlatFileHeaderMapping {
HEADER_EL(1),
HEADER_RESERVED1(5),
HEADER_RESERVED2(2),
HEADER_MESSAGE_TYPE(4)
public final int fieldSize;
private FlatFileHeaderMapping(int fieldSize) {
this.fieldSize = fieldSize;
}
}
which I can then use it place each line into a map and later access the keys in the map via this enum (like symbols)
Enumeration does not have this quality as far as I can see, and case classes are not ordered like the enum declarations - so cannot be used to match a record layout as shown above. At least not without the support of an ordered collection.
I could be missing something obvious, hence the question!
Thanks
Ray
overthink is right, but there's a less verbose way of declaring the case objects:
sealed abstract class FlatFileHeaderMapping(val fieldSize: Int)
case object HEADER_EL extends FlatFileHeaderMapping(1)
case object HEADER_RESERVED1 extends FlatFileHeaderMapping(5)
case object HEADER_RESERVED2 extends FlatFileHeaderMapping(2)
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping(4)
You could try using case objects:
sealed trait FlatFileHeaderMapping { val fieldSize: Int }
case object HEADER_EL extends FlatFileHeaderMapping { val fieldSize = 1 }
case object HEADER_RESERVED1 extends FlatFileHeaderMapping { val fieldSize = 5 }
case object HEADER_RESERVED2 extends FlatFileHeaderMapping { val fieldSize = 2 }
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping { val fieldSize = 4 }
You can then use the enum like so:
object Test {
def foo(x: FlatFileHeaderMapping) {
val result =
x match {
case HEADER_EL => "it's a HEADER_EL!"
case other => "its field size is: " + other.fieldSize
}
println(result)
}
def main(args: Array[String]) {
foo(HEADER_EL)
foo(HEADER_MESSAGE_TYPE)
}
}
The main nicety you get here is compile-time checking that all enum values are handled. i.e in the x match { ... } code above you'd get a compile error if you didn't have the 'case other => ...` clause in there.
I'm pretty much just restating this answer, which lists pros and cons of this approach.
object Direction extends Enumeration {
val North = Value("North")
val East = Value("East")
val South = Value("South")
val West = Value("West")
}
scala> import Direction._
scala> values foreach println
scala> val map = HashMap(North -> 1, South -> 2)
This is answered in Enumeration with constructor and lookup table
A simpler solution exist for integer value:
object FlatFileHeaderMapping extends Enumeration {
type FlatFileHeaderMapping = Value
val HEADER_EL = Value(1, "HEADER_EL")
val HEADER_RESERVED1 = Value(5, "HEADER_RESERVED1")
val HEADER_RESERVED2 = Value(2, "HEADER_RESERVED2")
val HEADER_MESSAGE_TYPE = Value(4, "HEADER_MESSAGE_TYPE")
}
Reproducing the contents of the accepted answer, as it's hidden behind a broken Tumblr link (that I accessed via Archive.org), which in turn points to this page.
trait Enum { //DIY enum type
import java.util.concurrent.atomic.AtomicReference //Concurrency paranoia
type EnumVal <: Value //This is a type that needs to be found in the implementing class
private val _values = new AtomicReference(Vector[EnumVal]()) //Stores our enum values
//Adds an EnumVal to our storage, uses CCAS to make sure it's thread safe, returns the ordinal
private final def addEnumVal(newVal: EnumVal): Int = { import _values.{get, compareAndSet => CAS}
val oldVec = get
val newVec = oldVec :+ newVal
if((get eq oldVec) && CAS(oldVec, newVec)) newVec.indexWhere(_ eq newVal) else addEnumVal(newVal)
}
def values: Vector[EnumVal] = _values.get //Here you can get all the enums that exist for this type
//This is the trait that we need to extend our EnumVal type with, it does the book-keeping for us
protected trait Value { self: EnumVal => //Enforce that no one mixes in Value in a non-EnumVal type
final val ordinal = addEnumVal(this) //Adds the EnumVal and returns the ordinal
def name: String //All enum values should have a name
override def toString = name //And that name is used for the toString operation
override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
override def hashCode = 31 * (this.getClass.## + name.## + ordinal)
}
}
//And here's how to use it, if you want compiler exhaustiveness checking
object Foos extends Enum {
sealed trait EnumVal extends Value /*{ you can define your own methods etc here }*/
val F = new EnumVal { val name = "F" }
val X = new EnumVal { val name = "X" }
}
/**
scala> Foos.values.find(_.name == "F")
res3: Option[Foos.EnumVal] = Some(F)
scala> Foos.X.ordinal
res4: Int = 1
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
}
<console>:10: warning: match is not exhaustive!
missing combination $anon$1
missing combination $anon$2
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
case Foos.F => println("dogpig")
}
doSmth: (foo: Foos.EnumVal)Unit
**/
//But if you don't care about getting exhaustiveness warnings, you can do:
object Foos extends Enum {
case class EnumVal private[Foos](name: String) extends Value /* { you can define your own methods and stuff here } */
val F = EnumVal("F")
val X = EnumVal("X")
}
/**
Which is a bit less boilerplatey.
Cheers,
√
**/