Can't restrict method by Lower-bound rule - scala

I've started to read about scala generics. Who can explain me why whit code work?
sealed abstract class Animal
class Cat extends Animal
class Dog extends Animal
class Box[A >: Animal] {
def set(a: A): A = ???
}
val catBox: Box[Animal] = new Box[Animal]
val dog = new Dog
catBox.set(dog)

I am guessing here, that what you mean by "does not work" is that you did not expect to be able to set a Dog into your catBox, because Dog is not a superclass of Animal.
This is expected though. Your definition of Box[Animal].set becomes
def set(a: Animal): Animal. Now, a Dog is an Animal, so, it satisfies the definition.
I don't quite understand what your intent is here. The type bound on Box restricts what kinds of boxes you can create:
new Box[Animal] // compiles
new Box[Dog] // does not compile - Dog is not a superclass of Animal
new Box[Any] // compiles - Any is a superclass of everything
but why would you want to restrict it like this doesn't make very much sense.
Perhaps, you wanted the upper bound instead:
class AnimalBox[A <: Animal]
val animalBox = new AnimalBox[Animal] // compiles
val dogBox = new AnimalBox[Dog] // compiles: Dog is a subclass of Animal
val catBox = new AnimalBox[Cat] // compiles: Cat is a subclass of Animal
val badBox = new AnimalBox[Any] // does not compile: Any is not a subclass
animalBox.set(new Dog) // compiles: Dog is an Animal
animalBox.set(new Cat) // compiles: Cat is an Animal
animalBox.set(new Pear) // does not compile: Pear is not an Animal
dogBox.set(new Dog) // compiles
dogBox.set(new Cat) // does not compile: cat is not a dog

Operator ??? is equivalent to throw new NotImplementedError.
So, when you call catBox.set(dog), you are throwing an exception.

Related

Difference of Upper Bounded Type and normal class behavior

I am studying Generics in Scala and I can't understand the difference between a "normal" class hierarchy and the Upper Bound type.
Looking at the example below: Cage can receive the class Animal, which means that I can pass either the class Animal or the class Dog. The same is valid for the upper bound parameter. What is the practical difference between them? When should I use one or the other?
class Animal
class Dog extends Animal
class Cage(animal: Animal)
val cage = new Cage(new Dog)
class AnotherCage[A <: Animal](animal: A)
val anotherCage = new AnotherCage(new Dog)
One difference is in the static type of animal parameter where in the former case it is typed as Animal whilst in latter case it is typed as Dog because the type parameter A is substituted with concrete type Dog . To see the difference try adding a sound method to Dog like so
class Animal
class Dog extends Animal {
def sound = "woof"
}
class Cage(val animal: Animal)
val cage = new Cage(new Dog)
class AnotherCage[A <: Animal](val animal: A)
val anotherCage = new AnotherCage(new Dog)
cage.animal.sound // error
anotherCage.animal.sound // ok
Note how compiler is not aware of sound method in the first case despite the fact that the runtime class referenced by animal argument is Dog.
Parameterized types can provide stronger type-safety and help avoid the need for type casting with asInstanceOf. For example, let's say we have a Dog and a Cat
class Animal
class Dog extends Animal
class Cat extends Animal
and we define a method that opens only cages containing Dogs
def openDogCage(cage: Cage): Dog =
if (cage.animal.isInstanceOf[Dog]) cage.animal.asInstanceOf[Dog]
else throw new IllegalArgumentException
def openAnotherDogCage(cage: AnotherCage[Dog]): Dog = cage.animal
but erroneously provide a cage with a Cat
val dog: Dog = openDogCage(new Cage(new Cat)) // runtime error
val dog: Dog = openAnotherDogCage(new AnotherCage(new Cat)) // compile-time error
then notice how parameterzied types caught the error at compile-time before the program even ran. Also notice how in definition of openDogCage when using just subtyping we had to manually perform a type cast with asInstanceOf to convince the compiler that method returns a Dog.

In Scala how do I define upper type bounds that are exclusive of the defined class?

Given a concrete class Animal, how do I define a function that only takes a subclass of Animal?
In typical examples like this Animal is a trait so defining [A <: Animal] implies that you already pass in a subclass of Animal. However, in a scenario like below where Animal is concrete, can I exclude that as being an allowed type?
I'm working with existing generated code, and this is just a generalized example of the problem. Therefore the implication is that I can't make Animal (or the equivalent) into a trait.
See below for an example:
class Animal {
def name: String = "General Animal"
}
class Dog extends Animal {
override def name: String = "Dog"
}
// How do I limit A to be a subtype of Animal (excluding Animal itself)?
class SpecificAnimalContainer[A <: Animal](a: A) {
def specificAnimal: A = a
}
val dogContainer = new SpecificAnimalContainer[Dog](new Dog)
// I do not want this to be able to compile.
val animalContainer = new SpecificAnimalContainer[Animal](new Animal)
Using shapeless you can write:
import shapeless._
class SpecificAnimalContainer[A <: Animal](a: A)(implicit ev: A =:!= Animal) {
def specificAnimal: A = a
}
// val animalContainer = new SpecificAnimalContainer[Animal](new Animal)// doesn't compile
Otherwise you can implement similar type for implicit yourself.
Type constraint for type inequality in scala
Enforce type difference
How can I have a negation type in Scala?
It's a bit unclear what you're trying to achieve, but your problem looks exactly like a book example from Scala documentation at
https://docs.scala-lang.org/tour/upper-type-bounds.html
abstract class Pet extends Animal {}
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
class Lion extends Animal {
override def name: String = "Lion"
}
// val lionContainer = new PetContainer[Lion](new Lion)
// ^this would not compile
Hope this helps

How do I create a List of classes?

I am trying to create a List of classes, like this:
abstract class Animal {
def name = this.getClass.getName
def say: String
}
class Dog extends Animal {
def say = "Woof"
}
class Cat extends Animal {
def say = "Meow"
}
val animalClasses = List(Dog, Cat)
This falls over on the last line, with the error message:
Zoo.scala:18: error: not found: value Dog
Note that I could create a List of instances of classes easily with List(new Dog(), new Cat()), but that's not what I want.
There you go:
scala> List(classOf[Dog],classOf[Cat])
res1: List[Class[_ >: Cat with Dog <: Animal]] = List(class Dog, class Cat)
As per this:
The predefined function classOf[T] returns a runtime representation of the Scala class type T.

Scala generics in an abstract function

There is an abstract class Animal.
Animal is extended by Dog and Cow.
Animal has an abstract function copy. When called on Dog is should return Dog and when called on Cow - Cow.
abstract class Animal[T] {
def copy[CT <: Animal[T]] (): CT
}
class Dog[T] extends Animal[T] {
def copy = new Dog[T]()
}
This gives an error. What am I doing wrong?
Your copy method in Dog does not have the same signature as the copy method in Animal (Animal has a type parameter and Dog does not), so the Scala compiler thinks you haven't implemented it for Dog. It looks like you're trying to work around the fact that copy should return the sub-type. You can use a self-type for this:
abstract class Animal[T] { self: T =>
def copy: T = this
}
class Dog extends Animal[Dog]
Unless you had something else in mind for the type parameter?
It might also be more prudent to use F-bounded polymorphism in this case, to verify that T is a sub-type of Animal.
abstract class Animal[T <: Animal[T]]
Essentially CT must be invariant for your approach to work. Your concrete implementation of Dog has no control over the type of CT, for example it's impossible to account for this with your approach (hence the compiler error):
new Dog[Int]().copy[Cow[Int]]()
Your concrete implementation gives me a Dog but I wanted a Cow. This is because your copy method cannot possibly satisfy the return type parameter variance. If all you need is one return type for the copy method you could employ this alternative (inspired by the same problem as it occurs in C++):
abstract class Animal[T, SubType <: Animal[T, _]] {
def copy (): SubType
}
class Dog[T] extends Animal[T, Dog[T]] {
override def copy() = new Dog[T]
}
val animal: Animal[Int, Dog[Int]] = new Dog()
val dogCopy: Dog[Int] = animal.copy()
Here's another simpler (not as obvious) approach. Scala permits you to override not just the implementation of a method, but also the return type to some degree:
abstract class Animal[T] {
def copy (): Animal[T]
}
class Dog[T] extends Animal[T] {
override def copy() = new Dog[T]
}
val dog = new Dog[Int]()
val dogCopy: Dog[Int] = dog.copy()

Liskov Substitution Principle and Arrays invariance

The Liskov Substitution Principle tells us that if A is a subtype of B than everything we can do with type B we should be able to do with type A.
So to investigate this further, I create the following:
class Animal
class Dog extends Animal
class BlueDog extends Dog
I understand why I am not allowed to do
val c: Array[Animal] = a
as Arrays are not covariant in Scala (like they are in Java).
But, I think I should be able to do:
val a: Array[Dog] = Array(new Dog())
val b: Array[BlueDog] = a
I would expect val b to be ok.
But I get:
class Array is invariant in type T. You may wish to investigate a wildcard type such as `_ >: ...
val a: Array[Dog] = Array(new Dog())
val b: Array[BlueDog] = a
Is a little strange, since your BlueDog is more strictly then Dog and may have other method.
class Animal
class Dog extends Animal
class BlueDog extends Dog {
def wolf() { println ("I'm a blue dog") }
}
Then what should the following code do?
val a: Array[Dog] = new Array(new Dog())
val b: Array[BlueDog] = a
b(0).wolf()
Well, your Dog in Array a does not have wolf() method....so it's clearly that you should not assign a parent type to subtype.
That's why the following works:
val dog: Dog = new BlueDog
But the following doesn't:
val blueDog: BlueDog = new Dog