Extract element of given subclass from list of superclass - scala

Given this code
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
class Pitbull extends Dog{}
object MyClass {
def main(args: Array[String]) {
val animals: List[Animal] = List(new Dog(), new Cat(), new Pitbull(), new Dog(), new Cat())
getElementOftype(animals, PitBull)
}
def getElementOftype(list: List[Animal], givenType: ???): Animal = {
}
}
I want to extract the first element of type Pitbull from this List, How should I proceed with this?
What I tried but feels wrong
trait Identity{
def who: String
}
class Animal extends Identity{
override def who = "Animal"
}
class Dog extends Animal with Identity{
override def who = "Dog"
}
class Cat extends Animal with Identity{
override def who = "Cat"
}
class Pitbull extends Dog with Identity{
override def who = "Pitbull"
}
object MyClass {
def main(args: Array[String]) {
val animals: List[Animal] = List(new Dog(), new Cat(), new Pitbull(), new Dog(), new Cat())
println(getElementOftype(animals, new Pitbull()))
}
def getElementOftype(list: List[Animal], givenType: Animal): Option[Animal] = {
list.collectFirst{case s if s.who == givenType.who => s}
}
}
I want to simply pass Pitbull as a parameter instead of instantiating an empty object.
This one works but I don't think it's the best approach.
There must be a more scalaesque way to do this.

You can do something like this (not tested, there may be some typos):
def getElementOftype[A <: Animal](list: List[Animal])(implicit ct: ClassTag[A]): Option[A] = {
list.collectFirst { case s: A => s }
}
The ClassTag is necessary to be able to match on the A type in collectFirst.

when you write Animal extends Identity you mean Animal IS an Identity, which is wrong, using this approach is preferred:
class Animal { self: Identity =>
}
which means whenever you want to instantiate an Animal, you need to give an Identity to it, but why would you want to do that?
You can simply use pattern matching to filter required type like this:
import scala.reflect.ClassTag
def getElementsOfType[A <: Animal : ClassTag](animals: List[Animal]): Option[A] = {
animals.filter {
case a: A => true
case _ => false
}.map(_.asInstanceOf[A]).headOption
}
headOption is because your api returns Option[A] as result, you can make it return List[A] and get rid of the headOption in case you have multiple Pitbull's inside the list :)

Related

Defining a generic to be a case class

In this example, I want the generic T to be a case class and a DAOEntity with id, so in the abstract implementation, I can use the copy method.
How to define it?
trait DAOEntity {
def id: String
}
// How to define this generic to force the use of a `case class` to have access to `copy`?
abstract class DAO[T <: DAOEntity] {
def storeInUppercase(entity: T): T = entity.copy(id = entity.id)
}
case class MyEntity(id: String) extends DAOEntity
class MyEntityDAO extends DAO[MyEntity] {
// Other stuff
}
There is no way to know if a type is a case class or not.
And even if there was, you won't get the copy method. The language doesn't provide a way to abstract over constructor; thus neither copy and factories (apply on companions) by extension. Which makes sense, what would be the type signature of such function?
What you can do instead is create a factory-like typeclass and ask for that:
trait DAOFactory[T <: DAOEntity] {
def copy(oldEntity: T, newId: String): T
}
object DAOFactory {
def instance[T <: DAOEntity](f: (T, String) => T): DAOFactory[T] =
new DAOFactory[T] {
override final def copy(oldEntity: T, newId: String): T =
f(oldEntity, newId)
}
}
Which can be used like this:
abstract class DAO[T <: DAOEntity](implicit factory: DAOFactory[T]) {
def storeInUppercase(entity: T): T =
factory.copy(
oldEntity = entity,
newId = entity.id.toUpperCase
)
}
And entities would provide the instance like this:
final case class MyEntity(id: String, age: Int) extends DAOEntity
object MyEntity {
implicit final val MyEntityFactory: DAOFactory[MyEntity] =
DAOFactory.instance {
case (oldEntity, newId) =>
oldEntity.copy(id = newId)
}
}
// This compile thanks to the instance in the companion object.
object MyEntityDAO extends DAO[MyEntity]
You can see the code running here.

How can I access the companion object of a Scala class passed as a type param?

I have a case class with companion object:
object Taco extends Dinner[Taco] {
def ingredientNames: Seq[String] = Seq("filling", "cheese", "sauce")
}
case class Taco(filling: Meat, cheese: Cheese, sauce: Sauce)
extends Dinner
And another one:
object Cheeseburger extends Dinner[Cheeseburger] {
def ingredientNames: Seq[String] = Seq("cheese", "bun", "condiments")
}
case class CheeseBurger(cheese: Cheese, bun: Bun, condiments: Seq[Condiment])
extends Dinner[Cheeseburger]
I need to get ingredient names for these dinners before actually creating any dinner instances:
def printMenu[D <: Dinner[D]]: String = ???
How can I access the companion object of a Dinner subclass?
Type classes to the rescue:
trait Dinner { ... }
trait DinnerCompanion[A <: Dinner] {
implicit def self: DinnerCompanion[A] = this
def ingredientNames: Seq[String]
...
}
object Taco extends DinnerCompanion[Taco] {
def ingredientNames: Seq[String] = Seq("filling", "cheese", "sauce")
}
case class Taco(filling: Meat, cheese: Cheese, sauce: Sauce) extends Dinner
def printMenu[A <: Dinner](implicit companion: DinnerCompanion[A]): String =
companion.ingredientNames.mkString(", ")
You probably want the following construction (inspired by GenericCompanion from the standard collection library):
type Condiment = String
type Meat = String
type Cheese = String
type Sauce = String
type Bun = String
trait Dinner[A] {
def companion: DinnerCompanion[A]
}
trait DinnerCompanion[A] {
def ingredientNames: Seq[String]
}
case class Taco(filling: Meat, cheese: Cheese, sauce: Sauce)
extends Dinner[Taco] {
def companion = Taco
}
implicit object Taco extends DinnerCompanion[Taco] {
def ingredientNames: Seq[String] = Seq("filling", "cheese", "sauce")
}
case class CheeseBurger(cheese: Cheese, bun: Bun, condiments: Seq[Condiment])
extends Dinner[CheeseBurger] {
def companion = CheeseBurger
}
implicit object CheeseBurger extends DinnerCompanion[CheeseBurger] {
def ingredientNames: Seq[String] = Seq("cheese", "bun", "condiments")
}
def printMenu[D: DinnerCompanion]: String =
implicitly[DinnerCompanion[D]].ingredientNames.mkString
Now every instance of a Dinner has method companion, and the companion in turn has ingredientNames.
EDIT added printMenu (has nothing to do with companion objects whatsoever, uses object Taco and object CheeseBurger as ordinary typeclass instances).

Scala typed trait different types Implementation keeping it in Map, not working

Is there any better design to achieve this scenario?
case class Animal()
case class Dog() extends Animal
case class Cow() extends Animal
trait BasePropertyImpl[T <: Animal] {
def property1(animal: T): T
def property2(animal: T): T
}
object DogPropertyImpl extends BasePropertyImpl[Dog] {
def property1(animal: Dog): Dog = ???
def property2(animal: Dog): Dog = ???
}
object CowPropertyImpl extends BasePropertyImpl[Cow] {
def property1(animal: Cow): Cow = ???
def property2(animal: Cow): Cow = ???
}
object AnimalImplRegistrar {
def getRegisteredAnimals: Map[String, BasePropertyImpl[Animal]] = Map(
"Dog" -> DogPropertyImpl,
"Cow" -> CowPropertyImpl,
)
}
object Main {
def main(args: Array[String]): Unit = {
val animal = AnimalImplRegistrar.getRegisteredAnimals.get("Dog").get
aminal.property1(Dog())
}
}
Here what I am trying to achieve is deferent implementation say CowPropertyImpl, DogPropertyImpl , or some more different implementations, I am keeping it in a Map and in runtime based on user input I am retrieving the implementation from Map and calling the method of that Impl class
This is a good candidate for the Type Class Pattern:
sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal
trait AnimalProperties[A] {
def property: A
}
object AnimalProperties {
implicit val dogProperties = new AnimalProperties[Dog.type] {
override def property: Dog.type = ???
}
implicit val catProperties = new AnimalProperties[Cat.type] {
override def property: Cat.type = ???
}
}
def f[A](a: A)(implicit ev: AnimalProperties[A]) = {
val a: A = ev.property
}
Based on the type of A (cat, dog) we get the desired properties of each animal.

How to create a factory for a parameterized class?

I'm trying to build a factory for implementations of a generic trait.
Given my domain model:
trait Person
case class Man(firstName: String, lastName: String) extends Person
case class Woman(firstName: String, lastName: String) extends Person
I created a repository for those classes like this:
trait Repository[T <: Person] {
def findAll(): List[T]
}
class ManRepository extends Repository[Man] {
override def findAll(): List[Man] = {
List(
Man("Oliver", "Smith"),
Man("Jack", "Russel")
)
}
}
class WomanRepository extends Repository[Woman] {
override def findAll(): List[Woman] = {
List(
Woman("Scarlet", "Johnson"),
Woman("Olivia", "Calme")
)
}
}
So far so good, some pretty simple classes. But I'd wanted to create a factory to create an instance of these repositories depending on some parameters.
object RepositoryFactory {
def create[T <: Person](gender: String): Repository[T] = {
gender match {
case "man" => new ManRepository()
case "woman" => new WomanRepository()
}
}
}
But this last piece won't compile. If I ommit the explicit return type of the factory, it compiles but returns a repository of type Repository[_1] instead of Repository[Man]
I can't seem to find a proper solution, do any of you guys have got some tips for me?
How about this?
Instead of using a string, use a sealed trait that knows how to create the appropriate repo
Use dependent types to return the correct type of repository
Code:
object GenderStuff {
trait Person
case class Man(firstName: String, lastName: String) extends Person
case class Woman(firstName: String, lastName: String) extends Person
// Note that Repository has to be covariant in P.
// See http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/ for explanation of covariance.
trait Repository[+P <: Person] {
def findAll(): List[P]
}
class ManRepository extends Repository[Man] {
override def findAll(): List[Man] = {
List(
Man("Oliver", "Smith"),
Man("Jack", "Russel")
)
}
}
class WomanRepository extends Repository[Woman] {
override def findAll(): List[Woman] = {
List(
Woman("Scarlet", "Johnson"),
Woman("Olivia", "Calme")
)
}
}
sealed trait Gender {
type P <: Person
def repo: Repository[P]
}
case object Male extends Gender {
type P = Man
def repo = new ManRepository()
}
case object Female extends Gender {
type P = Woman
def repo = new WomanRepository()
}
object RepositoryFactory {
def create(gender: Gender): Repository[gender.P] = gender.repo
// or if you prefer you can write it like this
//def create[G <: Gender](gender: G): Repository[G#P] = gender.repo
}
val manRepo: Repository[Man] = RepositoryFactory.create(Male)
val womanRepo: Repository[Woman] = RepositoryFactory.create(Female)
}
Of course, this makes the factory object pretty pointless - it's easier to just call Male.repo directly :)
You should probably think some more about what you're trying to achieve. In your example, what type would you expect to be returned by RepositoryFactory.create[Man]("woman")? The compiler has no way to associate those arbitrary strings with types.

Scala: Do implicit conversions work on Any?

I would like to store some objects from different type hierarchy into List[Any] or similar container, but perform implicit conversions on them later on to do something like type class.
Here is an example:
abstract class Price[A] {
def price(a: A): Int
}
trait Car
case class Prius(year: Int) extends Car
trait Food
case class FriedChicken() extends Food
object Def {
// implicit object AnyPrices extends Price[Any] {
// def price(any: Any) = 0
// }
// implicit object PriusPrices extends Price[Prius] {
// def price(car: Prius) = 100
// }
implicit object CarPrices extends Price[Car] {
def price(car: Car) = 100
}
implicit object FoodPrices extends Price[Food] {
def price(food: Food) = 5
}
}
def implicitPrice[A: Price](x: A) = implicitly[Price[A]].price(x)
import Def._
val stuff: List[Any] = List(Prius(2010), FriedChicken())
stuff map { implicitPrice(_) }
The above code throws an error as follows:
error: could not find implicit value for evidence parameter of type Price[Any]
stuff map { implicitPrice(_) }
^
If you uncomment AnyPrices, you'd get List(0,0), but that's not what I am expecting.
Do I have to store the manifest into the list for this to work?
Also, List(Prius(2010)) map { implicitPrice(_) } doesn't work either because it wants Price[Prius] and Price[Car] isn't good enough. Is there a way to make it more flexible?
So, looks like I can't get a type class once the objects are reduced to Any. My attempt of using Manifest also failed, since there seems to be no way for me to cast an Any into T even if I have the Manifest[T] object.
import reflect.Manifest._
def add [A, B >: A](stuff: A, list: List[(B, Manifest[_])])(implicit m: Manifest[A]) = (stuff, m) :: list
val stuff2 = add(Prius(2000), add(FriedChicken(), Nil))
stuff2 map { x =>
val casted = x._2.erasure.cast(x._1)
implicitPrice(casted)
}
gives me
error: could not find implicit value for evidence parameter of type Price[Any]
so it seems like I have to resolve things into Price before I stick them into List:
abstract class Price[A] {
def price(a: Any): Int
}
trait Car
case class Prius(year: Int) extends Car
trait Food
case class FriedChicken() extends Food
object Def {
implicit object PriusPrices extends Price[Prius] {
def price(car: Any) = 100
}
implicit object FriedChickenPrices extends Price[FriedChicken] {
def price(food: Any) = 5
}
}
import Def._
def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit p: Price[A]) = (stuff, p) :: list
val stuff = add(Prius(2000), add(FriedChicken(), Nil))
stuff map { x => x._2.price(x._1) }