How can I overload a Scala method by block type?
I'm still working on my ScalaTest FeatureSpec DSL.
I'd like 3 variants of my given
function. All take tokens: Any
, and then either
A. A block block: => Unit
that is executed later
given("user visits", the[AdminHomePage]) {
// just code
}
B. A block block: Any => Unit
that is executed later with the tokens
given("user visits", the[AdminHomePage]) {
x: Any => x match {
case ("user visits", pageClass:Class[Page]) =>
startPage(pageClass)
}
}
C. No block, where the tokens are processed by another function
given("user visits", the[AdminHomePage])
Now when I define all three methods
def given(tokens: Any) = ...
def given(tokens: Any)(block: Any => Unit) = block(tokens)
def given(tokens: Any)(block: => Unit) = block
The compiler considers them ambiguous.
ambiguous reference to overloaded definition, both method given in trait GivenWhenThenFeatureSpec of type (tokens: Any)(block: => Unit)Unit and metho开发者_JAVA百科d given in trait GivenWhenThenFeatureSpec of type (tokens: Any)(block: (Any) => Unit)Unit match argument types
How can disambiguate, or write a single method that can differentiate between the block (or lack of)?
I like @MachAndy's solution above, except for the importing of the unit2emptyfunction conversions, which I see as possibly interfering or covering other type errors.
If instead you define the following:
object Given {
trait Processor {
def process(tokens: Any)
}
class ProcessorA(block: =>Unit) extends Processor {
def process(tokens: Any) = {
block // execute or store block for later, ignoring tokens
}
}
class ProcessorB(block: Any=>Unit) extends Processor {
def process(tokens: Any) = {
block(tokens) // or store block for later execution
}
}
class ProcessorC extends Processor {
def process(tokens: Any) = {
// do something defaultish with the tokens
}
}
implicit def blockToProcessorA(block: =>Unit) = new ProcessorA(block)
implicit def blockToProcessorB(block: Any=>Unit) = new ProcessorB(block)
implicit val processorC = new ProcessorC
def given(tokens: Any)(implicit p: Processor) = p.process(tokens)
}
Then, you can simply:
import Given._
given("user visits", the[AdminHomePage])
given("user visits", the[AdminHomePage]) {
// some stuff that ignores tokens
}
given("user visits", the[AdminHomePage]) { x: Any =>
x match {
// do something that looks at the tokens
}
}
I have here a solution but I think it can be enhanced.
I used a single given
method as entry and implicit to provide or not a body
def given[A](tokens: A)(implicit block: A => Unit) {
block(tokens)
}
First here is a sugar to be able use a Unit
block as a Any => Unit
implicit def unit2emptyfunction(body: Unit): Any => Unit = {
case _ => body
}
To be able to work in the C case, I provide a default body to fill the block
parameter that does nothing.
implicit val doNothing: Any => Unit = { }
Now you can use it in this way :
/*
* A case, block is implicitly converted into a A => Unit
* although it doesn't use the argument
*/
given("user visits", the[AdminHomePage]) {
// just code
}
/*
* B case, block is fully provided and thus not implicitly converted
*/
given("user visits", the[AdminHomePage]) {
case ("user visits", pageClass: Class[Page]) =>
startPage(pageClass)
}
// C case, block implicitly provided by doNothing implicit val
given("user visits", the[AdminHomePage])
精彩评论