According to the andThen
method definition in PartialFunction trait in scala 12.13.6:
/** Composes this partial function with a transformation function that
* gets applied to results of this partial function.
*
* If the runtime type of the function is a `PartialFunction` then the
* other `andThen` method is used (note its cautions).
*
* @param k the transformation function
* @tparam C the result type of the transformation function.
* @return a partial function with the domain of this partial function,
* possibly narrowed by the specified function, which maps
* arguments `x` to `k(this(x))`.
*/
override def andThen[C](k: B => C): PartialFunction[A, C] = k match {
case pf: PartialFunction[B, C] => andThen(pf)
case _ => new AndThen[A, B, C](this, k)
}
/**
* Composes this partial function with another partial function that
* gets applied to results of this partial function.
*
* Note that calling [[isDefinedAt]] on the resulting partial function may apply the first
* partial function and execute its side effect. It is highly recommended to call [[applyOrElse]]
* instead of [[isDefinedAt]] / [[apply]] for efficiency.
*
* @param k the transformation function
* @tparam C the result type of the transformation function.
* @return a partial function with the domain of this partial function narrowed by
* other partial function, which maps arguments `x` to `k(this(x))`.
*/
def andThen[C](k: PartialFunction[B, C]): PartialFunction[A, C] =
new Combined[A, B, C](this, k)
That seems something new as in scala 2.12.14 we have one overload method for andThen
:
/** Composes this partial function with a transformation function that
* gets applied to results of this partial function.
* @param k the transformation function
* @tparam C the result type of the transformation function.
* @return a partial function with the same domain as this partial function, which maps
* arguments `x` to `k(this(x))`.
*/
override def andThen[C](k: B => C): PartialFunction[A, C] =
new AndThen[A, B, C] (this, k)
And in FSM.scala we have this method:
private[akka] def processEvent(event: Event, @unused source: AnyRef): Unit = {
val stateFunc = stateFunctions(currentState.stateName)
val nextState = if (stateFunc.isDefinedAt(event)) {
stateFunc(event)
} else {
// handleEventDefault ensures that this is always defined
handleEvent(event)
}
applyState(nextState)
}
That checks if stateFunc is defined at event then run it in stateFunc(event)
line
Also this is the definition of FSM transform
method:
final class TransformHelper(func: StateFunction) {
def using(andThen: PartialFunction[State, State]): StateFunction =
func.andThen(andThen.orElse { case x => x })
}
final def transform(func: StateFunction): TransformHelper = new TransformHelper(func)
that chains 2 partialFunctions.
So if we have a FSM actor with this definition:
def sideEffect(): Unit
def updateDB() : Unit
when(customStateName)(transform {
case Event(CustomEvent, CustomStateData) =>
sideEffect()
stay()
} using {
case FSM.State(_, NewSateData, _, _, _) =>
updateDB()
goto(nextState)
}))
So if this FSM actor receives CustomEvent
:
- sideEffect would be called 2 times
- updateDB would be called 1 times
Is it right?
We have the same logic in our application and seems was working well with scala 2.12.14 (both sideEffect and updateDB called 1 time), but after upgrading to scala 2.13.6 we see this new behaviour