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

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).

Related

Extract element of given subclass from list of superclass

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 :)

How to inherit generic trait multiple times in Scala?

I have a trait that looks like this:
trait Ingredient[T] {
def foo(t: T): Unit = {
// Some complex logic
}
}
And types for which I want to have methods:
class Cheese
class Pepperoni
class Oregano
How can I make another trait that has methods:
def foo(t: Cheese)
def foo(t: Pepperoni)
def foo(t: Oregano)
without duplicating the code? The following will not work as it has illegal inheritance from the same trait multiple times:
trait Pizza extends Ingredient[Cheese] with Ingredient[Pepperoni] with Ingredient[Oregano] {}
I'll offer 2 solutions:
If you are fine with defining Cheese, Pepperoni, and Oregano as traits, you can do:
trait Ingredient {
def foo[T <: Ingredient](t: T): Unit = {
println(t)
}
}
Then extending it:
trait Cheese extends Ingredient {
override def toString: String = "Cheese"
}
trait Pepperoni extends Ingredient {
override def toString: String = "Pepperoni"
}
trait Oregano extends Ingredient {
override def toString: String = "Oregano"
}
And the usage is:
trait Pizza extends Ingredient
val pizza = new Pizza { }
pizza.foo(new Cheese { })
pizza.foo(new Pepperoni { })
pizza.foo(new Oregano { })
Code run at Scastie.
Using sealed traits. This approach separates the ingredients from the final product which is binded in the question:
sealed trait Ingredient
sealed trait PizzaIngredient extends Ingredient
case object Cheese extends PizzaIngredient
case object Pepperoni extends PizzaIngredient
case object Oregano extends PizzaIngredient
case object Cucumber extends Ingredient
Then define the Pizza trait:
trait Pizza {
def foo[T <: PizzaIngredient](t: T): Unit = {
println(t)
}
}
And the usage is:
val pizza = new Pizza { }
pizza.foo(Cheese)
pizza.foo(Pepperoni)
pizza.foo(Oregano)
Code run at Scastie
The solution is to create objects that extend the Ingredient trait in Pizza trait. This way we have:
trait Pizza {
object CheeseIngredient extends Ingredient[Cheese]
object PepperoniIngredient extends Ingredient[Pepperoni]
object OreganoIngredient extends Ingredient[Oregano]
}
Then we can call our methods on inherited objects:
object LargePizza extends Pizza {
def bar(cheese: Cheese, pepperoni: Pepperoni, oregano: Oregano): Unit = {
CheeseIngredient.foo(cheese)
PepperoniIngredient.foo(pepperoni)
OreganoIngredient.foo(oregano)
}
}
Alternatively if we have only a few methods in Ingredient trait we can create overloads and encapsulate our supporting objects:
trait Pizza {
private object CheeseIngredient extends Ingredient[Cheese]
private object PepperoniIngredient extends Ingredient[Pepperoni]
private object OreganoIngredient extends Ingredient[Oregano]
def foo(cheese: Cheese): Unit = CheeseIngredient.foo(cheese)
def foo(pepperoni: Pepperoni): Unit = PepperoniIngredient.foo(pepperoni)
def foo(oregano: Oregano): Unit = OreganoIngredient.foo(oregano)
}
object LargePizza extends Pizza {
def bar(cheese: Cheese, pepperoni: Pepperoni, oregano: Oregano): Unit = {
foo(cheese)
foo(pepperoni)
foo(oregano)
}
}
The same could be achieved by just using imports in LargePizza. Adding overloads is better though as the client does not need to write additional imports and does not have the supporting objects in scope. The other benefit of using overloads is that the methods can be overridden in child classes.
If the intention is that Pizza needs all three of these ingredients, how about
class Pizza extends Ingredient[(Cheese, Pepperoni, Oregano)] {
def foo(ingredients: (Cheese, Pepperoni, Oregano)) = {
case (cheese, pepperoni, oregano) =>
// do something with them
}
}
Maybe you can do the following , you'll have to change the foo method :
trait Ingredient[T] {
def foo(ingredients: T*): Unit = {
for (i <- ingredients) {
// some complex logic
}
}
}
class Cheese
class Pepperoni
class Oregano
trait Pizza extends Ingredient[(Cheese,Pepperoni,Oregano)]

Get instance of singleton type in scala

I want to get the instance of a singleton type in Scala, is this possible?
Example: (I know it can be done easier in this case)
sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal
trait Person {
def name: String
// Is there a "FavoriteAnimal.instance" syntax?
def mostImportantThings = (FavoriteAnimal.instance, name)
protected type FavoriteAnimal <: Animal with scala.Singleton
}
case class DogPerson(override val name: String) extends Person {
override type FavoriteAnimal = Dog.type
}
case class CatPerson(override val name: String) extends Person {
override type FavoriteAnimal = Cat.type
}
Using shapeless.Witness correct syntax is
sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal
trait Person {
def name: String
def mostImportantThings(implicit
witness: Witness.Aux[FavoriteAnimal]
): (FavoriteAnimal, String) = (witness.value, name)
protected type FavoriteAnimal <: Animal with scala.Singleton
}
case class DogPerson(override val name: String) extends Person {
override type FavoriteAnimal = Dog.type
}
case class CatPerson(override val name: String) extends Person {
override type FavoriteAnimal = Cat.type
}
DogPerson("A Dog Person").mostImportantThings // (Dog, A Dog Person)
Unfortunately in the current version of Shapeless (2.3.3) there is a bug and this code doesn't compile. But after fix it does.
Maybe you want something like
sealed trait Animal
case object Dog extends Animal
case object Cat extends Animal
trait Person[A <: Animal] {
def name: String
def animal: A
def mostImportantThings = (animal, name)
}
case class DogPerson(override val name: String) extends Person[Dog.type] {
override val animal = Dog
}
case class CatPerson(override val name: String) extends Person[Cat.type] {
override val animal = Cat
}

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.

Using trait method in the class constructor

I have a trait and a class that extends the trait. I can use the methods from the trait as follows:
trait A {
def a = ""
}
class B(s: String) extends A {
def b = a
}
However, when I use the trait's method in the constructor like this:
trait A {
def a = ""
}
class B(s: String) extends A {
def this() = this(a)
}
then the following error appears:
error: not found: value a
Is there some way to define default parameters for the construction of classes in the trait?
EDIT: To clarify the purpose: There is the akka-testkit:
class TestKit(_system: ActorSystem) extends { implicit val system = _system }
And each test looks like this:
class B(_system: ActorSystem) extends TestKit(_system) with A with ... {
def this() = this(actorSystem)
...
}
because I want to create common creation of the ActorSystem in A:
trait A {
val conf = ...
def actorSystem = ActorSystem("MySpec", conf)
...
}
It's a little bit tricky because of Scala initialization order. The simplest solution I found is to define a companion object for your class B with apply as factory method:
trait A {
def a = "aaaa"
}
class B(s: String) {
println(s)
}
object B extends A {
def apply() = new B(a)
def apply(s: String) = new B(s)
}