Comparisons between generic types in Scala - scala

Consider the following segment of code -
abstract class Vehicle {
val name: String
}
case class Car(name: String) extends Vehicle
case class Truck(name: String) extends Vehicle
abstract class VehicleContainer[T <: Vehicle] {
def compare(that: VehicleContainer[T]): Int
}
class CarContainer(wheels: Int) extends VehicleContainer[Car] {
override def compare(that: CarContainer): Int = ???
}
The intention here is to have a compare method on the VehicleContainer that can be defined for each specific instance of VehicleContainer. The compare method comparison clause needs to be unique for each instance, because it could be comparing using attributes specific to that instance and hence not defined in the abstract base class VehicleContainer.
The trouble is that this does not work in it's current form, i.e. the override for compare is illegal. What I am not able to understand is how to accomplish this - define a base class that indicates that the sub classes need to implement a compare method, where the method argument to that compare method is that sub class itself. Would appreciate a pointer to the right concept here if it's some straightforward generics concept that I am missing here.
Thanks!

One way to solve your problem could be use of F-bounded polymophism. You would just need one additional type parameter:
abstract class Vehicle {
val name: String
}
case class Car(name: String) extends Vehicle
case class Truck(name: String) extends Vehicle
abstract class VehicleContainer[T <: Vehicle, V <: VehicleContainer[T, V]] {
def compare(that: V): Int
}
class CarContainer(wheels: Int) extends VehicleContainer[Car, CarContainer] {
override def compare(that: CarContainer): Int = ???
}

Related

scala class extend a trait with generic which is a type of a field

I want to write code like this:
trait A[S]
class B {
class BI
}
class C(val b: B) extends A[b.BI] // won't compile
Which won't compile.
So I write this:
class C[BI0] private (val b: B) extends A[BI0]
object C {
def apply(b: B): C[b.BI] = new C(b)
}
But that looks ugly. Is there a better implementation?
Why do I have this question? I conceive a example:
trait Store[Goods] {
def sell(goods: Goods): Unit
}
class CarFactory {
def make(): Car = new Car
class Car
}
class CarStore(val factory: CarFactory) extends Store[factory.Car]{//can't compile
def sell(car: factory.Car): Unit = {}
}
I don't want to use CarFactory#Car because this car store only sell cars of the factory factory.
I don't think there's a better way. What you have is a fairly standard way of working around using path-dependent types in class declarations. Alternatively, you can use type members:
trait A { type S }
class B { class BI }
class C(val b: B) extends A { type S = b.BI }
I'm not entirely sure what are you trying to do here, but does this work for you?
trait A[S]
class B {
class BI
}
class C(val b: B) extends A[B#BI]

Passing parameters to a trait

I want to model the game of chess.
For that, I want to make an abstract class, Piece which takes a player and a position as arguments. From that, I want to extend to other classes such as Pawn:
trait Piece(player: Int, pos: Pos) = {
def spaces(destination: Pos): List[Pos]
}
case class Pawn extends Piece = {
//some other code
}
However, I think I'm not allowed to pass parameters to a trait, like this trait Piece(player: Int, pos: Pos).
So how can I have an abstract class Piece that has fields?
You could use an abstract class
abstract class Piece(player: Int, pos: Pos) {
...
}
case class Pawn(player: Int, pos: Pos) extends Piece(player, pos)
Or (probably better) you define those members abstractly in a trait
trait Piece {
def player: Int
def pos: Pos
...
}
case class Pawn(player: Int, pos: Pos) extends Piece
Dotty allows traits to have parameters, just like classes have parameters.
trait Greeting(val name: String) {
def msg = s"How are you, $name"
}
class C extends Greeting("Bob") {
println(msg)
}
I wasn't happy with the accepted answer for my use-case so I did the following. Note that you have two possibilities here for this same approach:
trait Piece {
// these can be referred to within this trait to implement reusable code
val player: Int
val pos: Pos
def spaces(destination: Pos): List[Pos] = {
// use player and pos at will e.g.
List(pos, destination)
}
}
case class Pawn(playerArg: Int, posArg: Pos) extends Piece = {
// now override those with whatever you like
override val player: Int = playerArg
override val pos: Pos = posArg
//some other code
}
The second alternative is to use and override methods instead e.g. def getPlayer: Int.
Yet another possibility is to use implicit on the trait methods that would require accessing those attributes but I'm not a big fan of this approach.
That said, apparently they have been thinking about it SIP-25 Trait Parameters.

Enforce that all subclasses implement a given type class

Given a superclass or trait, and assuming an open hierarchy, how can I enforce that all extending classes implement a particular type class?
For instance, assuming the type class Default
trait Default[T] { def default: T }
and some trait Super:
trait Super { }
I would like to enforce that the following (by itself) is not allowed:
class A(val i: Int) extends Super
...while the following is:
class B(val i: Int) extends Super
implicit val bHasDef = new Default[B] { def default = B(42) }
Assuming the above is possible, can I then access the type class evidence for the subtypes from a method within Super? I.e, something like:
trait Super {
def magic: Default[this.type] = ???
}
I hardly think you can enforce that, at least in a simple enough way, maybe it's possible with something more complex like shapeless.
What I would do is add some modifications to the super trait and make it take a self reference to Default
trait Default[T] { def default: T }
trait Super[T] {
self: Default[T] =>
}
class B(val i: Int) extends Super[Int] with Default[Int] {
override def default: Int = ???
}
class A(val i: Int) extends Super[Int] // doesn't compile, needs a Default
This should also solve the second part of your question, the disadvantage is that now one trait is bundled to the other.

Scala: lock extended class

I'd like to "lock" a class, which is extended from a trait. Is it possible in Scala?
For example I have:
trait A {
val boris: String
val john: String
val number: Int
}
class B extends A {
// do something with these values
}
but can I ensure, that in class B no new values will be added if those aren't declared in trait A?
Thanks for your answers.
You cannot.
But if you simply mark the trait as sealed and provide a default implementation:
sealed trait A { val boris: String }
final class B(val boris: String) extends A {}
then people are free to create implicit value classes that make it look like new functionality has been added (except without actually creating the class):
implicit class MyB(val underlying: B) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B("fish")).sirob // "hsif"
You can also let the classes take a type parameter as a marker if you want to keep them straight at compile-time (though not runtime):
sealed trait A[T] { val boris: String }
final class B[T](val boris: String) extends A[T] {}
implicit class MyB(val underlying: B[Int]) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B[Int]("fish")).sirob // "hsif"
(new B[Char]("fish")).sirob // error: value sirob is not a member of B[Char]
So you could--especially with 2.10--simply lock everything and let users enrich the original interface this way.
I'm not sure if this covers your intended use case, though; it doesn't provide any inheritance.
Based on your example and my guess at what you are actually trying to do, you may want to consider just using case classes.
Extending a case class is generally avoided (I think it will spit out deprecation warnings if you try), so that will prevent people from wanting to extend your class in order to add functionality.
Translating your example into a case class:
case class A (boris: String, john: String, number: Int)
Then instead of extending A to change its values, you'd just make a new instance, e.g.
val a2 = someOtherA.copy(john="Doe")

Scala: Restricting Ordered comparison in subclasses to the same subclass

I'm trying to compare poker hands as shown below. I've been playing around with different type operators but would be interested in some guidance. My goal is to have an abstract parent class that declares Ordered (so that it doesn't need to be declared on each subclass), but the parameterization would be such that each subclass can only be compared with an instance of the same class.
For example, below, a HighCard can only be compared with another HighCard, TwoPair with another TwoPair, etc.
sealed abstract class HandValue(rank: Int) extends Ordered[?]
case class HighCard(high: Int) extends HandValue(0){
def compare(that: HighCard) = ...
}
case class TwoPair(high: Int, big: Int, sm: Int) extends HandValue(2) {
def compare(that: TwoPair) = ...
}
F-bounded polymorphism is one common way to accomplish this kind of thing:
sealed abstract class HandValue[A <: HandValue[A]](rank: Int) extends Ordered[A]
case class HighCard(high: Int) extends HandValue[HighCard](0){
def compare(that: HighCard) = ...
}
case class TwoPair(high: Int, big: Int, sm: Int) extends HandValue[TwoPair](2) {
def compare(that: TwoPair) = ...
}
It may feel a bit like boilerplate to have to hand yourself as a type parameter to the thing you're extending, but it's a very convenient way to be able to talk specifically about subclass types in the parent.