How to restrict generic trait in subtype - scala

I have a trait Mutable[T] that describes objects that can be mutated to T using a Mutation object:
trait Mutable[T] {
def mutate(mutation: Mutation): T
}
class Mutation {
def perform[T <: Mutable[T]](mutable: T): T = mutable.mutate(this)
}
I also have two traits describing animals in general, as well as specifically mammals.
I would like to require that an Animal can mutate into another Animal, but a Mammal can only mutate into another Mammal. However, the following does not compile:
trait Animal extends Mutable[Animal]
trait Mammal extends Animal, Mutable[Mammal]
case class Fish() extends Animal {
override def mutate(mutation: Mutation): Animal = Fish()
}
// error: class Monkey cannot be instantiated since it has conflicting base types Mutable[Animal] and Mutable[Mammal]
case class Monkey() extends Mammal {
override def mutate(mutation: Mutation): Mammal = Human()
}
// error: class Human cannot be instantiated since it has conflicting base types Mutable[Animal] and Mutable[Mammal]
case class Human() extends Mammal {
override def mutate(mutation: Mutation): Mammal = Monkey()
}
I would like to use these types as follows:
val mutation = new Mutation()
val fish: Animal = Fish()
val fish2: Animal = mutation.perform(fish)
val monkey: Mammal = Monkey()
val monkey2: Mammal = mutation.perform(monkey)

Don't you want to make Mutable covariant?
trait Mutable[+T] {
def mutate(mutation: Mutation): T
}
In such case your code seems to compile in Scala 3
https://scastie.scala-lang.org/qVMDsu7HRLiBFlSchGxWEA
When you were loosening the restriction on Mutation.mutate to [T <: Mutable[? <: T]] Mutable[? <: T] is actually defining covariance at a call site
In Scala3, if generic type argument(s) is mapped to dependent type, how are covariant & contravariant modifiers mapped?
Also you can try to make T a type member rather than type parameter. In such case the existential type is just Mutable while a specific type is Mutable { type T = ... } (aka Mutable.Aux[...])
trait Mutable:
type T
def mutate(mutation: Mutation): T
object Mutable:
type Aux[_T] = Mutable { type T = _T }
class Mutation:
def perform[M <: Mutable](mutable: Mutable): mutable.T = mutable.mutate(this)
trait Animal extends Mutable:
type T <: Animal
trait Mammal extends Animal:
type T <: Mammal
case class Fish() extends Animal:
type T = Animal
override def mutate(mutation: Mutation): Animal = Fish()
case class Monkey() extends Mammal:
type T = Mammal
override def mutate(mutation: Mutation): Mammal = Human()
case class Human() extends Mammal:
type T = Mammal
override def mutate(mutation: Mutation): Mammal = Monkey()
val mutation = new Mutation()
val monkey: Mammal = Monkey()
val monkey2: Mammal = mutation.perform(monkey)
val monkey3: Mammal = mutation.perform[Mammal](monkey)
val fish: Animal = Fish()
val fish2: Animal = mutation.perform(fish)
val fish3: Animal = mutation.perform[Animal](fish)
Bind wildcard type argument in Scala (answer)
Also you can try a type class
// type class
trait Mutable[T]:
type Out
def mutate(t: T, mutation: Mutation): Out
class Mutation:
def perform[T](t: T)(using mutable: Mutable[T]): mutable.Out = mutable.mutate(t, this)
trait Animal
trait Mammal extends Animal
case class Fish() extends Animal
object Fish:
given Mutable[Fish] with
type Out = Fish
def mutate(t: Fish, mutation: Mutation): Out = Fish()
case class Monkey() extends Mammal
object Monkey:
given Mutable[Monkey] with
type Out = Human
def mutate(t: Monkey, mutation: Mutation): Out = Human()
case class Human() extends Mammal
object Human:
given Mutable[Human] with
type Out = Monkey
def mutate(t: Human, mutation: Mutation): Out = Monkey()
val mutation = new Mutation()
val monkey: Monkey = Monkey()
val monkey2: Human = mutation.perform(monkey)
val monkey3: Human = mutation.perform[Monkey](monkey)
val fish: Fish = Fish()
val fish2: Fish = mutation.perform(fish)
val fish3: Fish = mutation.perform[Fish](fish)
Although type class instances have to be resolved statically while you seem to prefer resolving values dynamically (val fish: Animal = Fish, val monkey: Mammal = Monkey).
https://docs.scala-lang.org/tutorials/FAQ/index.html#how-can-a-method-in-a-superclass-return-a-value-of-the-current-type
http://tpolecat.github.io/2015/04/29/f-bounds.html
Advantages of F-bounded polymorphism over typeclass for return-current-type problem

The error is caused by a conflict between Mutable[Animal] and Mutable[Mammal] in the list of supertypes for Monkey.
To resolve the conflict, you can instead use Mutable[? <: Animal] which is compatible with Mutable[Mammal]:
trait Animal extends Mutable[? <: Animal]
trait Mammal extends Animal, Mutable[Mammal]
Then, to extend the type Animal, you have to explicitly add Mutable[Animal] as a supertype to be able to override the mutate method:
case class Fish() extends Animal, Mutable[Animal] {
override def mutate(mutation: Mutation): Animal = Fish()
}
It might be helpful to define a helper trait to save a bit of typing:
trait AnimalBase extends Animal, Mutable[Animal]
case class Fish() extends AnimalBase {
override def mutate(mutation: Mutation): Animal = Fish()
}
However, the type of Animal now no longer matches the requirement for Mutation.perform, even if you explicitly specify the type parameter:
val mutation = new Mutation()
val monkey: Mammal = Monkey()
val monkey2: Mammal = mutation.perform(monkey) // OK
val monkey3: Mammal = mutation.perform[Mammal](monkey) // OK
val fish: Animal = Fish()
val fish2: Animal = mutation.perform(fish) // error: Found: (fish : Animal), Required: Nothing
val fish3: Animal = mutation.perform[Animal](fish) // error: Type argument Animal does not conform to upper bound Mutable[Animal]
This can be fixed by loosening the restriction on Mutation.mutate to [T <: Mutable[? <: T]]:
class Mutation {
def perform[T <: Mutable[? <: T]](mutable: T): T = mutable.mutate(this)
}
val mutation = new Mutation()
val fish: Animal = Fish()
val fish2: Animal = mutation.perform(fish) // OK
val fish3: Animal = mutation.perform[Animal](fish) // OK

Related

Unable to extend type bounds in scala for implementations

I want to define an abstract variable with type bound in trait and implement in the respective extended class. Something similar to this
trait Animal {
def makeSound(): Unit
}
class Lion extends Animal {
override def makeSound(): Unit = println("Roar")
def scarySound(): Unit = println("Rooooaaarrrrr")
}
trait AnimalCostume {
val animal: _ <: Animal
def locate(): Unit = {
animal.makeSound()
}
}
class LionCostume extends AnimalCostume {
override val animal = new Lion()
def scareEveryOne(): Unit = animal.scarySound()
}
The code is not compiling with the following exception
unbound wildcard type
val animal: _ <: Animal
Can you help in understanding how do I achieve something like this and why it is failing with error that type is not bounded if I have explicitly defined the upper bound 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).

Why does this Scala code not compile?

I do not understand why this does not compile. It's complaining that friend which is of Mammal type does not have a talk method. But there is...
class Animal {
def talk:String = "i am animal"
}
class Mammal extends Animal {
override def talk:String = "i am mammal"
}
class Cow extends Mammal {
override def talk:String = "moo"
}
trait Farm[+A] {
def set[AA >: A](friend: AA): String
}
class CowFarm extends Farm[Cow] {
override def set[Mammal >: Cow](friend: Mammal): String = friend.talk
}
In set Mammal is not the same Mammal that you declared a few lines above. It's a local generic type parameter that shadowed your existing Mammal. So your method signature is actually not different to:
override def set[AA >: Cow](friend: AA): String = friend.talk
Since AA is a supertype of Cow, it is not guaranteed to have a talk method. For example, it can be substituted with Any or AnyRef.

Abstract Over TypeClass

Starting with some simple code:
trait Moveable[A] {
def move(a: A): A
}
trait Animal {
def kick[A <: Animal: Moveable](a: A): A = implicitly[Moveable[A]] move a
}
object Cat {
implicit object CatMoveable extends Moveable[Cat] {
def move(cat: Cat): Cat = cat copy (pos = cat.pos + 4)
}
}
case class Cat(pos: Int) extends Animal
case class Dog(pos: Int) extends Animal
val someAnimal: Animal = Dog(0)
val kickedCat: Cat = someAnimal kick Cat(0)
println(kickedCat) // Cat(4)
I decided to distinguish between, let´s say, Quadruped and Biped animals:
trait FourFeetMoveable[A] {
def moveWithFourFeets(a: A): A
}
trait TwoFeetMoveable[A] {
def moveWithTwoFeets(a: A): A
}
trait Animal {
def kick[A <: Animal /*: ??? */](a: A): A
}
trait Quadruped extends Animal {
def kick[A <: Animal: FourFeetMoveable](a: A): A = implicitly[FourFeetMoveable[A]] moveWithFourFeets a
}
trait Biped extends Animal {
def kick[A <: Animal: TwoFeetMoveable](a: A): A = implicitly[TwoFeetMoveable[A]] moveWithTwoFeets a
}
object Chicken {
implicit object ChickenTwoFeetMoveable extends TwoFeetMoveable[Chicken] {
def moveWithTwoFeets(chicken: Chicken): Chicken = chicken copy (pos = chicken.pos + 2)
}
}
case class Dog(pos: Int) extends Quadruped
case class Chicken(pos: Int) extends Biped
val someAnimal: Animal = Dog(0)
val kickedChicken: Chicken = someAnimal kick Chicken(0)
println(kickedChicken) // Chicken(2)
A must have is to have two totally different typeclasses FourFeetMoveable and TwoFeetMoveable, so I can´t abstract over them with something like this:
trait Moveable[A] {
def move(a: A): A
}
So how can I abstract over the typeclasses used as a context bound at method kick in trait Animal(see the ???)?
EDIT
Sorry, I should have made my example clearer. Lets say the effect of being kicked can be some movement or some other action. I wanted to abstract over that effect with a typeclass.
In the following code I am showing what I mean and also used an abstract type member KickingEffect to abstract over the required typeclass, as 0__ proposed:
trait StumbleEffect[A <: Animal] {
def stumble(a: A): A
}
trait GlideEffect[A <: Animal] {
def glide(a: A): A
}
trait Animal {
type KickingEffect[A <: Animal]
def kick[A <: Animal: KickingEffect](a: A): A
}
trait Biped extends Animal {
type KickingEffect[A <: Animal] = StumbleEffect[A]
override def kick[A <: Animal: StumbleEffect](a: A): A = implicitly[StumbleEffect[A]] stumble a
}
trait Quadruped extends Animal {
type KickingEffect[A <: Animal] = GlideEffect[A]
override def kick[A <: Animal: GlideEffect](a: A): A = implicitly[GlideEffect[A]] glide a
}
object Dog {
implicit object DogGlideEffect extends GlideEffect[Dog] {
def glide(dog: Dog): Dog = dog copy (pos = dog.pos + 4)
}
}
case class Dog(pos: Int) extends Quadruped
case class Cat(pos: Int) extends Quadruped
case class Chicken(pos: Int) extends Biped
But then I ran into another problem, when it comes to sequences of animals:
type Beast[A <: Animal, KE[_ <: Animal]] = A { type KickingEffect[X <: Animal] = KE[X] }
val dogBeast: Beast[Dog, GlideEffect] = Dog(0) // fine
type GlideBeasts[A <: Quadruped] = Beast[A, GlideEffect]
val glideBeasts: Seq[GlideBeasts[Quadruped]] = Seq(Dog(0), Cat(0)) // fine
def kickAll[A <: Animal, KE[_ <: Animal], KA <: Animal](kicker: Beast[A, KE])(animals: Seq[KA])(implicit ev: kicker.KickingEffect[KA]): Seq[KA] = {
for (a <- animals) yield kicker kick a
}
val cat = Cat(0)
val dog = Dog(0)
kickAll(cat)(Seq(dog)) // wrong inferred kinds of type arguments
kickAll[Cat, GlideEffect, Dog](cat)(Seq(dog)) // missing implicit evidence
Like this?
trait Moveable[A] {
def move(a: A): A
}
trait FourFeetMoveable[A] extends Moveable[A]
trait TwoFeetMoveable[A] extends Moveable[A]
trait Animal {
type CanKick[A] <: Moveable[A]
def kick[A <: Animal : CanKick](a: A): A = implicitly[CanKick[A]] move a
}
trait Quadruped extends Animal {
type CanKick[A] = FourFeetMoveable[A]
}
trait Biped extends Animal {
type CanKick[A] = TwoFeetMoveable[A]
}
Regarding your edit: I would recommend at this point not to further try to model that with types, unless it is really an extremely crucial point in your application or a pure thought experiment. You can get easily overambitious with type-safe design, then the ratio between design effort and application value is getting to big; I would just drop some compile-time safety and go for pattern matching and runtime errors.
If you do want to follow the types route, as soon as you have collections, you will need something like HLists to preserve the individual types of the collection members.
Anyway, you can make your example work (with explicit type parameters):
def kickAll[A <: Animal, KE[_ <: Animal], KA <: Animal](
kicker: Beast[A, KE])(animals: Seq[KA])(implicit effect: KE[KA]): Seq[KA] = {
for (a <- animals) yield kicker kick a
}
val cat = Cat(0)
val dog = Dog(0)
kickAll(cat)(Seq(dog)) // still doesn't figure out the types
kickAll[Cat, GlideEffect, Dog](cat)(Seq(dog)) // ok!
As said, the tricky or impossible part comes when you try to do this with heterogeneous lists (e.g. requiring different effects). You might get away with using a helper type class for the elements of the sequence, instead, so that implicits can be resolved per item before.
As a side note, I found it always useful to not introduce type bounds till the moment they are really needed. Not only do you safe a lot of typing (no pun intended), but you keep options open (e.g. for later variance annotations). The following is totally sufficient:
trait StumbleEffect[A] {
def stumble(a: A): A
}
trait GlideEffect[A] {
def glide(a: A): A
}
trait Animal {
type KickingEffect[A]
def kick[A : KickingEffect](a: A): A
}
trait Biped extends Animal {
type KickingEffect[A] = StumbleEffect[A]
override def kick[A : StumbleEffect](a: A): A =
implicitly[StumbleEffect[A]] stumble a
}
trait Quadruped extends Animal {
type KickingEffect[A] = GlideEffect[A]
override def kick[A : GlideEffect](a: A): A = implicitly[GlideEffect[A]] glide a
}
etc.

Scala: Making concrete classes of trait with type member that has a representation type

This is a follow-up to a previous question where I had a trait Garage with a type member CarType, which itself had a type member FuelType, and I needed a function refuel that could take an instance of CarType as the first argument and an instance of the first argument's FuelType as the second argument.
The answer, the two traits below, was to give Car a representation type C <: Car[C]. The problem that I now have is that I can't figure out how to define the type parameter on the concrete classes implementing Garage, e.g. ConcreteGarage below.
trait Fuel
trait Garage {
type CarType <: Car[CarType]
def cars: Seq[CarType]
def copy(cars: Seq[CarType]): Garage
def refuel(car: CarType, fuel: CarType#FuelType): Garage = copy(
cars.map {
case `car` => car.refuel(fuel)
case other => other
})
}
trait Car[C <: Car[C]] {
type FuelType <: Fuel
def fuel: FuelType
def copy(fuel: C#FuelType): C
def refuel(fuel: C#FuelType): C = copy(fuel)
}
class ConcreteGarage(val cars: Seq[ConcreteGarage#CarType]) extends Garage {
type CarType = Car[CarType] // Nope
//type CarType = Car[Any] // Nope
//type CarType = Car[Nothing] // Nope
//type CarType = Car[Car] // Nope
//type CarType <: Car[CarType] // Nope
def copy(cars: Seq[CarType]) = new ConcreteGarage(cars)
}
That's why I said beware of the ugliness of having to carry around another type parameter for the rest of your life :)
class ConcreteGarage[C <: Car[C]](val cars: Seq[C]) extends Garage {
type CarType = C
def copy(cars: Seq[C]) = new ConcreteGarage(cars)
}
Of course, its simpler if you have a particular garage.
case class Benzin(litres: Int) extends Fuel
case class Mustang(fuel: Benzin) extends Car[Mustang] {
type FuelType = Benzin
def copy(fuel: Benzin) = Mustang(fuel)
}
case class MustangGarage(cars: Seq[Mustang]) extends Garage {
type CarType = Mustang
def copy(cars: Seq[Mustang]) = MustangGarage(cars)
}
val m = Mustang(Benzin(0))
val g0 = MustangGarage(Seq(m))
val g1 = g0.refuel(m, Benzin(45))