Scala inner class typing

71 Views Asked by At

Suppose I have the following:

class Deck[+T] {
    class Card(value: T)
    class Pile(val cards: List[Card]) {
        val deck = Deck.this
        def shuffle(shuffler: Shuffler): shuffler.shuffle(this)
    }
}

trait Shuffler {
    def shuffle[T](pile: Deck[T]#Pile): pile.type
}

object Shuffler {
    def randomShuffler(r: Random): Shuffler = new Shuffler {
        override def shuffle[T](pile: Deck[T]#Pile): pile.deck.Pile = {
            new pile.deck.Pile(r.shuffle(pile.cards))
        }
    }
}

Is it possible to do the same thing without having the val deck declaration in Pile? Also, is it possible to do the same thing without the T declaration in shuffle()?

I had been playing around with things such as pile: x.Pile forSome {val x: Deck[_]}, but they don't seem to compile due to typing issues (read: me not fully understanding semantics therein), and I'm trying to avoid rewriting Shuffler to, say, work with raw lists instead (how do I express that, anyways? List[Deck[T]#Card] is not quite there, since I want lists of Cards from the same Deck).

1

There are 1 best solutions below

0
On

Is it possible to do the same thing without having the val deck declaration in Pile?

Not if we want to enforce a value dependent type, which you seem to want (e.g. two Pile[Int] being only compatible if they refer to the same deck value).

Also, is it possible to do the same thing without the T declaration in shuffle()?

You can move type parameters to type members, this can sometimes save you from needing to specify them when they are only internally used.

Here is an idea:

object Pile {
  def apply(deck0: Deck): Pile { type D = deck0.type } = new Pile {
    val deck  = deck0
    type D    = deck0.type
    val cards = deck.cards.toList
  }
}
trait Pile { self =>
  type D <: Deck
  type Self = Pile { type D = self.deck.type }

  val deck : D
  def cards: List[deck.Card]

  def shuffle(shuffler: Shuffler): Self = shuffler.shuffle(this)
}

object Deck {
  def apply[A1](values: Set[A1]): Deck { type A = A1 } = new Deck {
    type A = A1
    val cards = values.map(Card(_))
  }
}
trait Deck {
  type A

  case class Card(value: A)

  def cards: Set[Card]
}

trait Shuffler {
  def shuffle(pile: Pile): pile.Self
}

 

object Shuffler {
  def randomShuffler(r: util.Random): Shuffler = new Shuffler {
    def shuffle(pile: Pile): pile.Self = new Pile {
      type D    = pile.deck.type
      val deck  = pile.deck
      val cards = r.shuffle(pile.cards)
    }
  }
}

Test:

val deck  = Deck(Set(1 to 10: _*))
val pile0 = Pile(deck)
pile0.cards
val sh    = Shuffler.randomShuffler(util.Random)
val pile1 = pile0.shuffle(sh)
pile1.cards

As you can see, enforcing value dependent types is not trivial, so the question is if you really need them, or you are ok with a simple type parameter for A. For example, the above doesn't prevent you from accidentally putting the same card twice into a pile.