How to make my handle method polymorphic when using traits - scala

I want my handle method to be able to take any Animal type and then be able to actually have the specific type so I can continue processing.
I just sketched out what I am looking for:
sealed trait Animal
case class Dog(id: Int) extends Animal
case class Cat(id: Int) extends Animal
case class Owl(id: Int) extends Animal
object AnimalHandler {
def handle(animal: Animal) = animal match {
case Dog => processDog(dog)
case Cat => processCat(cat)
}
}
How can I design my domain to work like the above?

The code you have pretty much works as-is.
The syntax on the pattern match would need to include a variable name, and if you're actually using the attributes of the case classes you would probably use the pattern to extract them.
sealed trait Animal
case class Dog(id: Int) extends Animal
case class Cat(id: Int) extends Animal
case class Owl(id: Int) extends Animal
def processDog(dog: Dog) = println(s"woof ${dog.id}")
def processCat(id: Int) = println(s"meow $id")
def processOwl(owl: Owl, id: Int) = println(s"hoot ${owl.id}")
object AnimalHandler {
def handle(animal: Animal) = {
case dog: Dog => processDog(dog)
case Cat(id) => processCat(id)
case owl # Owl(id) => processOwl(owl, id)
}
}
Some notes:
given a sealed trait, the compiler will warn you if your pattern match isn't complete (in your case you were missing handling for Owl).
if the body of a method/function is nothing but a pattern match on the argument you can skip the animal match

Related

How to return the concrete Product type from my function?

I have different product types like:
sealed trait Product
case class Downloadable(url: String) extends Product
case class Shippable(weight: Int) extends Product
case class Virtual(name: String) extends Product
object Load {
def get(id: Int): Product = id match {
case 1 => Downloadable("google.com")
case 2 => Shippable(100)
case _ => Virtual("virtuallll")
}
}
println(Load.get(1))
println(Load.get(2))
println(Load.get(3))
val d = Load.get(1)
println(d.url) // error value url is not a member
I don't have access to the properties of the concrete type, is it possible to be able to?
Pattern matching.
Load.get(1) match {
case dl:Downloadable => println(s"URL: ${dl.url}")
case shp:Shippable => println(s"weight: ${shp.weight}")
case vt:Virtual => println(s"name: ${vt.name}")
}
Each variable (dl, shp, vt) exists only within its case code block.
You are trying to deal uniformly with instances that are not uniform. The solution is to make them uniform, that's what base classes and traits are for: defining and describing common interface.
sealed trait Product[T] { def data: T }
case class Downloadable(url: String) extends Product[String] { def data = url }
case class Shippable(weight: Int) extends Product[Int] { def data = weight }
case class Virtual(name: String) extends Product[String] { def data = name }
then you can do println(Product.get(1).data)
Alternatively (and I don't really recommend that, but if it is like the only place in the codebase where you need this, implementing it properly is too complicated) you can exploit the fact that case classes are instances of Product (not your Product, but scala library trait representing a cartesian product).
All Product instances have a method productIterator that lets you iterate through values of all member variables. So, you can just do this with your existing definitions:
println(Product.load(1).productIterator.next)

Combine related ADTs in a type safe way

I'm designing a typesafe api to work with a "types" -- an abstraction in the application I'm working on. Here is how it looks like:
sealed trait EnumType
case object A extends EnumType
case object B extends EnumType
case object C extends EnumType
sealed abstract class TypeInfo[T <: EnumType](val enumType: T)
case class Ainfo() extends TypeInfo(A)
case class Binfo() extends TypeInfo(B)
case class Cinfo() extends TypeInfo(C)
sealed trait TypeMeta[T <: EnumType]
case class Ameta() extends TypeMeta[A.type]
case class Bmeta() extends TypeMeta[B.type]
case class Cmeta() extends TypeMeta[C.type]
case class TypeDescription[T <: EnumType](info: TypeInfo[T], meta: TypeMeta[T])
I'm confused about defining a function which would accept a List of TypeInfo and return TypeDescription. I currently implemeted it as follows:
//Type parameter with omitted bound? Is that type safe?
def toDescription(lst: List[TypeInfo[_]]): List[TypeDescription[_]] = {
lst map {
case a: Ainfo => TypeDescription(a, Ameta())
case b: Binfo => TypeDescription(b, Bmeta())
case c: Cinfo => TypeDescription(c, Cmeta())
}
}
To workaround the issue I used [_] pattern which does not look typesafely. Is there a way to redeclare the function?
Its type safe , however the 2 type parameters are bound individually to their own constraints and not to one another.
If you are looking to do that , I think you would need to define a method type parameter like so
//Type parameter with omitted bound? Is that type safe?
def toDescription[T<:EnumType](lst: List[TypeInfo[T]]): List[TypeDescription[T]] = {
lst map {
case a: Ainfo => TypeDescription(a, Ameta())
case b: Binfo => TypeDescription(b, Bmeta())
case c: Cinfo => TypeDescription(c, Cmeta())
}
}
Now if you tried to write
case a: Ainfo => TypeDescription(a, Bmeta())
you will get a compilation error

Exhaustive match on possible object values

First the code:
object MyEnums {
sealed abstract class MyEnum(val value: String)
case object First extends MyEnum("Some_ugly_looking_value1")
case object Second extends MyEnum("Some_ugly_looking_value2")
case object Third extends MyEnum("Some_ugly_looking_value3")
case object Fourth extends MyEnum("Some_ugly_looking_value4")
def fromString(value: String): Option[MyEnum] =
value match {
case First.value => Option(First)
case Second.value => Option(Second)
case Third.value => Option(Third)
case Fourth.value => Option(Fourth)
case _ => None
}
}
What I'm trying to achieve here is to be able to parse a string value coming from the outside into the form of the above enum. At the same time I would like to have the exhaustive pattern matching compiler warning if I don't cover all options in the match expression. What options do I have here? I don't like what I implemented above, since if this enum grows I may just forget to implement the new case clause...
Consider enumeratum like so
import enumeratum._
sealed abstract class MyEnum(override val entryName: String) extends EnumEntry
object MyEnum extends Enum[MyEnum] {
val values = findValues
case object First extends MyEnum("Some_ugly_looking_value1")
case object Second extends MyEnum("Some_ugly_looking_value2")
case object Third extends MyEnum("Some_ugly_looking_value3")
case object Fourth extends MyEnum("Some_ugly_looking_value4")
}
MyEnum.withName("Some_ugly_looking_value1") // res1: MyEnum = First
Now we do not have to fiddle with pattern match when adding a new case object.

Scala best practice on using self in method

HI I have a case when calling a method to use self. Now not sure the best practice to do this in Scala. I have created a example of how I'm doing it just wanted to ask is this the best way of doing so.
sealed trait Animal {
// match on self
final def speak(): Unit = {
this match {
case Cat(name) => println("spoken like a Cat named: " + name)
case Dog(name) => println("spoken like a Dog named: " + name)
case Pig(name) => println("spoken like a Pig named: " + name)
}
}
final def whoAmI(): Unit = {
this match {
case Cat(_) => println("I am a Cat")
case Dog(_) => println("I am a Dog")
case Pig(_) => println("Could be a Pig")
}
}
}
final case class Cat(name: String) extends Animal
final case class Dog(name: String) extends Animal
final case class Pig(name: String) extends Animal
If your requirement is only to know which sub-type is used, it can be accessed in a more straightforward manner with just this.getClass():
sealed trait Animal {
val name: String
final def speak(): Unit =
println(s"spoken like a ${this.getClass.getSimpleName} named: ${name}")
final def whoAmI(): Unit =
println(s"I am a ${this.getClass.getSimpleName}")
}
If all the implementing sub-types are characterized by a name, that would be convenient to declare an abstract val name: String, which will allow to access the field in speak().
For a use case like this the matchers are not the best option: for each implementing sub-type you have to add an entry in the matchers (may become difficult to maintain). I'd suggest using inheritance: if you have a behavior differentiated among the sub-types, declare an abstract method in the trait, call it from it, but its implementations should remain at sub-types level.
Yes, your use of pattern matching on this is correct.
Since your hierarchy is sealed, you know, that there will not be any new types to account for - otherwise you should have the _ => ... clause.
You can express your second case a bit simpler, since you dont care about the parameters, only about the type.
final def whoAmI(): Unit = {
this match {
case _:Cat => println("I am a Cat")
case _:Dog => println("I am a Dog")
case _:Pig => println("Could be a Pig")
}
}
Finally, this always refers to the innermost type. In case of nested inner classes, you might want to use an alias, like self:
trait Tweeter {
this: self => // reassign this into self, so it can be accessed from an inner class

Scala: var to val - redesign

In my code base, I want to refractor away from vars. The code base structure follows the format:
class Animal {
var name : Option[String] = None
var owner : Option[String] = None
}
case class Dog(breed: String) extends Animal {
//Dog logic
}
The main reason for this design is that information is not available all at the same time.
So, from service A, which deserializes a json, I receive an Animal (either Animal, Dog)
val animal = new Animal
animal.name = "My little animal"
and then
def update(animal: Animal) : Unit {
animal match {
case a : Animal => a.owner = Some("Joe") // call to service B
case _ => //handle dog
}
}
update(animal)
The question is: How can I redesign it to avoid mutable state in Animal?
Write a copy method in Animal? There are a bunch of fields, which might generate some boilerplate
Composition?
Making the two classes case?
case class Animal (...)
case class Dog(animal: Animal, breed: String)
Edit
Animal as a trait
I still need a concrete implementation of Animal, as I will have Dogs and Animals (which are really Animal and no subtype)
Problems with copy
The built in case class copy method, does NOT update the values of the Animal class. So defaults values will be used - not what I want. - yes, it is possible to create a case class with all the fields, but is it practical if we have 100+ fields?
You could design this as an ADT (Abstract Data Type) like this:
trait Animal {
def name: Option[String]
def owner: Option[String]
}
case class Dog(dog: Option[String] = None, owner: Option[String] = None) extends Animal
With a case class you have the copy method by default!
A good solution could be to make a trait which specifies Animal behavior,
sealed trait Animal {
val name: Option[String]
val owner: Option[String]
}
and then make case classes to provide type constructors for your animal instances.
case class Dog(name: Option[String], owner: Option[String]) extends Animal
Now, update (changeOwner) will take an Animal and return another Animal likewise:
def changeOwner(animal: Animal): Animal = animal match {
case Dog(name, owner) => Dog(name, Some("Joe"))
case _ => animal
}
And you will use it as follows:
val dog: Animal = Dog(Some("Doggie"), Some("Jack"))
val newDog: Animal = changeOwner(dog)
Also, from your code:
case a : Animal => a.owner = Some("Joe") // call to service B
Be careful with this, as the first case of your match will take all animals and thus the other cases, for example, your Dog case, will be unreachable.