Reputation: 2347
I there, I'm trying to combine the Command and Chain of responsibility patterns with Scala style.
Basically, I would like to have one Executor
who pass the command through the chain and return the result. A command is anything that return a T
:
class class Invoker {
type Command[T] = () => T
val handlers = new mutable.ListBuffer[PartialFunction[() => T, T]]
def invoke[T](command: => T): Future[T] = Future {
handlers.find(_.isDefinedAt(command)).map(_.apply(command))
}
}
But, because I'm a scala beginner, I have problems with the generic types for the list of handlers
. I cannot figure how to define T
in the declaration of handlers
so that the invoke
command return the correct type (which should be T
)
Can someone help me to implement this pattern?
Upvotes: 0
Views: 334
Reputation: 7162
There are several sketchy places, but I think the closest you'll get to what you want would be (Though, it will not work, see below):
import scala.collection.mutable
import scala.concurrent.Future
object Invoker {
val handlers = new mutable.ListBuffer[PartialFunction[Any, Any]]
def invoke[T](command: () => T): Future[T] = Future {
handlers.collectFirst {
case pf if pf.isDefinedAt(command) => pf(command)
}.fold(throw new Exception("failure")) {
_.asInstanceOf[T]
}
}
}
However,
It will not work because, the partial functions or more specifically pattern matching will most certainly not work as you expect for Function0
You loose most of your type information through erasure and have to rely upon what you know about the partial functions.
In scala, the need to call asInstanceOf
is a good indicator that something can be done better.
@1
When you define a list of partial functions that match on Function0
like for example this:
val T1: () => Int = () => 1
val T2: () => Int = () => 2
val T3: () => Int = () => 3
val pfs: Seq[PartialFunction[Any, Any]] = Seq(
PartialFunction[Any, Any] {
case T1 => T1()
},
PartialFunction[Any, Any] {
case T2 => T2()
},
PartialFunction[Any, Any] {
case T3 => T3()
}
)
Trying to find a match for any function literal will lead to a MatchError:
def invoke[T](c: () => T): T = {
pfs.collectFirst {
case pf if pf.isDefinedAt(c) => pf(c)
}.fold(throw new Exception("failure")) {
_.asInstanceOf[T]
}
}
invoke(() => 1) |-> scala.MatchError: <function0> (of class abc.A$A290$A$A290$$anonfun$ti1$1)
This would only work if you define Constants for allowed functions and only use those constants, when you call invoke.
You could define them with the Invoker
object:
object Invoker {
val SomeCommand = () => 5 + 5
val AnotherCommand = () => 5 * 5
}
But that would utterly eliminate any flexibility.
FINALLY:
If you want to be able to perform any Command then why maintain a list of handlers anyway?
object Invoker {
def invoke[T](command: () => T): Future[T] = Future {
command()
}
}
should be sufficient for that.
Upvotes: 2