Reputation: 4231
I have an inner case class, specifically an event from this question, and want to match it, including the outer object:
class Player {
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def execute() = _life += life
}
}
I can easily write an effect (a partial function) that replaces life events for a specific player:
//gain twice as much life
def effect(player: Player): ReplacementEffect = {
case player.GainLife(x) => player.GainLife(x * 2)
}
however, I can't do the same for other players. The closest I have come is this:
//only you gain life
def effect2(player: Player): ReplacementEffect = {
case evt: Player#GainLife => player.GainLife(evt.life)
}
But 1) this replaces even your own lifegain with a new lifegain, 2) I can't reference the player that originally gained life in the function and 3) I'm missing out on directly matching life
this way.
This could be expressed using a path-independent type like
case Player.GainLife(_player, life) if _player != player => GainLife(player, life)
Ideally, I want something like
case _player.GainLife(life) if _player != player => player.GainLife(life)
Is this possible somehow, or can I work around this? Or do I have to resort to making GainLife nested?
Upvotes: 1
Views: 554
Reputation: 4231
The closest I have come is to define my own unapply
method:
class Player {
self =>
var _life = 20
def life = _life
def gainLife(life: Int) = execute(GainLife(life))
case class GainLife(life: Int) extends Event {
def player = self
def execute() = _life += life
}
}
object Player {
object _GainLife {
def unapply(event: Player#GainLife) =
Some((event.player, event.life))
}
}
Note that naming the Player._GainLife
object Player.GainLife
instead would cause a name conflict, that is the most important downside here. Therefore, I chose to make that type available from outside the Player
namespace:
val GainLife = Player._GainLife
This allows to match using both player.GainLife.unapply and Player._GainLife.unapply in a concise way:
//gain twice as much life
def effect1(player: Player): ReplacementEffect = {
case player.GainLife(life) => player.GainLife(life * 2)
}
//only you gain life
def effect2(player: Player): ReplacementEffect = {
case GainLife(_player, life) if _player != player => player.GainLife(life)
}
//all players gain twice as much life
def effect3: ReplacementEffect = {
case GainLife(player, life) => player.GainLife(life * 2)
}
The last two examples look a little asymmetric, but that can be fixed with an apply
method if desired.
Upvotes: 0
Reputation: 11479
When you define the class inside of another it means that the type is specific to the surrounding class, so playerA.GainLife is not the same type as playerB.GainLife (this is called path dependent types) if you want it to mean the same thing you define it in a scope that is the same regardless of instance: the package or the companion object of your class.
You can read more in this question: What is meant by Scala's path-dependent types?
Upvotes: 5