我試圖在斯卡拉實現一個StateMachine,但是我遇到了一個讓我非常困惑的類型系統的問題。在下面的代碼中,我需要讓後衛函數接受StateMachine的期望子類的參數。不幸的是,由於函數N的參數是逆變的,所以我不確定如何解決這個問題。通過函數參數的協方差
class Transition[S, +M <: StateMachine[S]](start: S, end :S, var guard: Option[M => Boolean]) { // COMPILER ERROR ABOVE LINE : ^^ covariant type M occurs in contravariant position in type => Option[M => Boolean] of method guard ^^ val startState = start val endState = end def willFollow(stateMachine: M, withGuard : Boolean) = // COMPILER ERROR ABOVE : ^^ covariant type M occurs in contravariant position in type M of value stateMachine ^^ if (!withGuard && guard == None) true; else (withGuard && guard.get(stateMachine)) } class EpsilonTransition[S, M <: StateMachine[S]](start: S,end :S) extends Transition[S, M](start, end, None) class StateMachine[S](transitions: Set[Transition[S, StateMachine[S]]], initialStates: Set[S]) { private val stateDrains = transitions.groupBy(_.startState); private var activeStates = initialStates def act() = { var entryStates = Set[S]() var exitStates = Set[S]() stateDrains.foreach {drain => val (exitState, transitionsOut) = drain // Follow non-epsilon transitions transitionsOut.filter(_.willFollow(this, true)).foreach {transition => exitStates += transition.startState entryStates += transition.endState } } // For all exit states we map the state to a set of transitions, and all sets are "flattened" into one big set of transitions // which will then filter by those which do not have a guard (epsilon transitions). The resulting filtered list of transitions // all contain endStates that we will map to. All of those end states are appended to the current set of entry states. entryStates = entryStates ++ (exitStates.flatMap(stateDrains(_)).filter(_.willFollow(this, false)).map(_.endState)) // Exclude only exit states which we have not re-entered // and then include newly entered states activeStates = ((activeStates -- (exitStates -- entryStates)) ++ entryStates) } override def toString = activeStates.toString } object HvacState extends Enumeration { type HvacState = Value val aircon, heater, fan = Value } import HvacState._ object HvacTransitions { val autoFan = new EpsilonTransition[HvacState, HVac](aircon, fan) val turnOffAc = new Transition[HvacState, HVac](aircon, fan, Some(_.temperature 75)) val HeaterToFan = new Transition[HvacState,HVac](heater, fan, Some(_.temperature > 50)) } import HvacTransitions._ class HVac extends StateMachine[HvacState](Set(autoFan, turnOffAc, AcToHeater, HeaterToAc, HeaterToFan), Set(heater)) { var temperature = 40 }
什麼原因特徵過渡是用+ M參數聲明的? –
你爲什麼要'M'協變?只是好奇。 –
M是協變的(+ M),因爲如果我不把它標記爲第三行到代碼snipplet的結尾,HVac類擴展StateMachine [HVacState] ...將顯示所有轉換的錯誤我傳遞給StateMachine構造函數。每個錯誤都說明,雖然我們有參數HVac的轉換,但預計會出現StateMachine [HVacState]的轉換。 –