1

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) = ...
    }
Roy
  • 3,574
  • 2
  • 29
  • 39

1 Answers1

3

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.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • Great, thanks for including the article as well! One other thing: now if I want to compare them, what is the proper way to cast them? For example, if I have two `HandValues`, h1 and h2, `h1 > h2` doesn't compile since `h2` presumably needs to be casted. I gave it a shot with `h1 > h2.asInstanceOf[h1.getClass]`, or do I add the type to the `>` function itself? – Roy Aug 06 '12 at 00:33
  • What this approach buys you is specifically compile-time safety: if two values aren't comparable, the compiler won't let you compare them. Once you start casting things you lose that safety. You may want to use something like pattern matching on the values to make sure they're the same kind of hand, or to use a different approach altogether (like a [`PartialOrdering`](http://www.scala-lang.org/api/current/scala/math/PartialOrdering.html)). – Travis Brown Aug 06 '12 at 00:42
  • Thanks! This is slightly unrelated, but what's the syntactical error I'm making when I do this: `val (h1, h2): (HandValue[_], HandValue[_]) = (handValue(hands._1), handValue(hands._2))`, which yields `error: type $_2 not found`, while the following compiles correctly: `val h1: HandValue[_] = handValue(hands._1); val h2: HandValue[_] = handValue(hands._2)` – Roy Aug 06 '12 at 01:02