What is the current recommended pattern for family polymorphism in Scala?
While experimenting with ways of modeling games, this solution recently emerged:
trait Game[G <: Game[G]] {
type PLAYER <: Player[G]
type STATE <: State[G]
def players(): Set[G#PLAYER]
def startState(): G#STATE
}
trait Player[G <: Game[G]]
trait State[G <: Game[G]] {
def player(): G#PLAYER
}
A specific game (Poker in this example) can be expressed in terms of those traits like so:
class Poker() extends Game[Poker] {
type PLAYER = PokerPlayer
type STATE = PokerState
val p1 = new PokerPlayer()
def players() = Set(p1)
def startState(): PokerState = ...
}
class PokerPlayer() extends Player[Poker]
class PokerState() extends State[Poker] {
def player(): PokerPlayer = ...
}
I have several questions about this setup:
How is Game[G <: Game[G]]
pronounced in English? What are the names for the roles that G
and Game
are playing in this situation? (Meaning specifically in this "recursive" relationship.)
Is this a reasonable implementation of "family polymorphism"? At a high level, my understanding is that this means that Game and it's PLAYER and STATE must vary as a "family". The takes on family polymorphism in Scala that I've seen elsewhere differ substantially, and I'm not clear on the different tradeoffs:
Scala Overview (2006) http://www.scala-lang.org/docu/files/ScalaOverview.pdf
Martin Kneissl blog (2009) http://www.familie-kneissl.org/Members/martin/blog/family-polymorphism-in-scala
Discussion of approaches to family polymorphism involving typeclasses, macros, f-bounded polymorphism, or anything else are welcome.
Polymorphism is the ability of any data to be processed in more than one form. The word itself indicates the meaning as means many and. means types. Scala implements polymorphism through virtual functions, overloaded functions and overloaded operators.
Scala has 3 types of Polymorphism that we will explore further below. These are called subtype , parametric and ad-hoc .
It means an abstract type member is defined (inside some context, e.g. a trait or class), so that concrete implementations of that context must define that type.
A Trait is a concept pre-dominantly used in object-oriented programming, which can extend the functionality of a class using a set of methods. Traits are similar in spirit to interfaces in Java programming language. Unlike a class, Scala traits cannot be instantiated and have no arguments or parameters.
I would question the need to define the classes "outside" of the trait at all. The types Player
and State
are already dependent on Game, so you don't need to try to restrict it further.
trait Game {
type Player
type State <: StateLike
trait StateLike {
def player: Player
}
def startState: State
}
class Poker extends Game {
class Player
class State extends StateLike { ... }
val startState = new State
}
You can use the cake pattern to separate different parts out to different traits/files if you want.
trait PokerPlayer extends Game {
class Player
}
trait PokerState extends Game with PokerPlayer {
class State extends StateLike { ... }
}
class Poker extends Game with PokerPlayer with PokerState {
val startState = ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With