ScalaTest - writing custom matchers - scala

I am running into a problem while writing a custom matcher for NodeSeq:
private def matchXML(expected: NodeSeq) = new Matcher[NodeSeq] {
def apply(left: NodeSeq): MatchResult = MatchResult(left xml_== expected,
"XML structure was not the same (watch spaces in tag texts)",
"XML messages were equal")
}
This compiles, but the following piece of code:
val expected : NodeSeq = ...
val xml : NodeSeq = ...
xml should matchXML(expected)
causes:
error: overloaded method value should with alternatives:
(beWord: XMLStripDecoratorTests.this.BeWord)XMLStripDecoratorTests.this.ResultOfBeWordForAnyRef[scala.collection.GenSeq[scala.xml.Node]] <and>
(notWord: XMLStripDecoratorTests.this.NotWord)XMLStripDecoratorTests.this.ResultOfNotWordForAnyRef[scala.collection.GenSeq[scala.xml.Node]] <and>
(haveWord: XMLStripDecoratorTests.this.HaveWord)XMLStripDecoratorTests.this.ResultOfHaveWordForSeq[scala.xml.Node] <and>
(rightMatcher: org.scalatest.matchers.Matcher[scala.collection.GenSeq[scala.xml.Node]])Unit
cannot be applied to (org.scalatest.matchers.Matcher[scala.xml.NodeSeq])
xml should (matchXML(expected))
Any ideas what this means?

Why this fails to typecheck:
The type checker works in the following way.
xml.should(matchXML(expected))
Because the method should is not part of a NodeSeq, the compiler tries to find an implicit conversion for xml to a ShouldMatcher.
The book "Programming in Scala" specifies that such implicit conversion should be the most specific:
"Up through Scala 2.7, that was the end of the story. Whenever
multiple implicit conversions applied, the compiler refused to choose
between them. ... Scala 2.8 loosens this rule. If one of the available
conversions is strictly more specific than the others, then the
compiler will choose the more specific one. ... one implicit conversion
is more specific than another if one of the following applies: The
argument type of the former is a subtype of the latter’s. .."
Because NodeSeq extends Seq[Node], the following function
convertToSeqShouldWrapper[T](o : scala.GenSeq[T]) : SeqShouldWrapper[T]
is therefore the most specific one among all others.
The program is rewritten as:
`convertToSeqShouldWrapper(xml).should(matchXML(expected))`
where convertToSeqShouldWrapper(xml) is a SeqShouldWrapper[T] where T = GenSeq[Node].
The method should from SeqShouldWrapper accepts a Matcher[T] which is a function of type T => MatchResult. Therefore, it accepts a Matcher[GenSeq[Node]].
Because T is appearing to the left of the arrow, matchers are not covariant in T, but contravariant. A NodeSeq is a GenSeq[Node], so a Matcher[GenSeq[Node]] is a Matcher[NodeSeq], not the opposite.This explains the above error, where the method should cannot accept a Matcher[NodeSeq] and requires a Matcher[GenSeq[Node]].
2 Solutions
Replace All instances of NodeSeq to GenSeq[Node] so that the type matches everywhere.
Alternatively, wrap xml explicitely with the conversion function.
convertToAnyShouldWrapper(xml).should(matchXML(expected))

This looks to me like your matchXML method is not in scope.

Related

Why can't the compiler select the correct String.contains method when using this lambda shorthand?

Say I want to check if a string contains any of the letters in "cory":
def hasCory(input: String): Boolean = {
val myName = "cory"
input.exists(myName.contains)
}
The compiler complains with:
error: type mismatch;
found : CharSequence => Boolean
required: Char => Boolean
Scala provides the Char-accepting method I want in StringOps:
But it appears that the compiler cannot see this method unless I change the code to one of:
input.exists(myName.contains(_))
input.exists(c => myName.contains(c))
Instead in the original example it appears to be using Java String's contains method, which indeed does accept a CharSequence:
Is this working as intended? Why can't the compiler see that I want the Char version of contains?
StringOps is an implicit conversion
#inline implicit def augmentString(x: String): StringOps = new StringOps(x)
And implicit conversion are applicable in three cases only:
If an expression 𝑒 is of type 𝑇, and 𝑇 does not conform to the expression's expected type pt.
In a selection 𝑒.π‘š
with 𝑒 of type 𝑇, if the selector π‘š does not denote an accessible member of 𝑇.
In a selection 𝑒.π‘š(args)
with 𝑒 of type 𝑇, if the selector π‘š denotes some member(s) of 𝑇, but none of these members is applicable to the arguments args.
When you write myName.contains it's none of the three cases (in particular,
not the 2nd case because contains is an accessible member of String) so StringOps can't be applied and it's String#contains(CharSequence) and type mismatch error.
When you write myName.contains(_) or c => myName.contains(c) it's the 3rd case so StringOps can be applied and it's StringOps#contains(Char) after implicit conversion.
So yes, it's working as intended.

Scala Implicit syntax in polymorphic methods

I am a Scala noob reading through a parsing library, and have reached some syntax I do not understand:
def parseA[_: P] = P("a")
val Parsed.Success(value, successIndex) = parse("a", parseA(_))
I want to be able to combine these lines into one, ie
val Parsed.Success(value, successIndex) = parse("a", P("a"))
but this gives a compile error:
Error:(8, 61) overloaded method value P with alternatives:
[T](t: fastparse.P[T])(implicit name: sourcecode.Name, implicit ctx: fastparse.P[_])fastparse.P[T] <and>
=> fastparse.ParsingRun.type
cannot be applied to (String)
Error occurred in an application involving default arguments.
val Parsed.Success(value, successIndex) = parse(source, P("a"))
How should this line be written? And can you name the syntax concepts involved to maximise my learning?
_: P is the same as (implicit ctx: P[_]), that means that method is asking for an implicit parameter of type P[_] (the underscore means that it does not care for the inner type. See What are all the uses of an underscore in Scala?).
P("a") is calling this method, which requires such implicit in scope, and that is why in your second example it fails to compile, because it did not find the implicit parameter.
The features sued here are implicits, existential types & macros...
All of them are very advanced techniques. If you are just starting, I would suggest to leave them for latter.
Implicits are very important and useful, I would start from there, but first make sure you feel comfortable with "normal" Scala (whatever that means).
For the second question, I think this should work.
def program[_: P] = parse("a", P("a"))
val Parsed.Success(value, successIndex) = program

Applying type constructors to generated type parameters with Scala macros

I am trying to materialize an instance of the (simplified) trait
trait TC[F[_]] {
def apply[A](fa: F[A]): F[A]
}
using Scala macros. The signature of the macro therefore is
def materialize[F[_]](c: Context)(
implicit fT: c.WeakTypeTag[F[_]]): c.Expr[TC[F]]
Type constructor F[_] now needs to be applied to the type parameter A for two reasons:
To write the signature of apply above for a particular F (like Foo[A])
To inspect the members of the type Foo[A] in order to specify an interesting body of apply
Is there any way to create the type corresponding to the method type parameter A that can than be used in appliedType? It appears difficult for me, since the method apply and its type parameter A are also just being generated as trees.
I tried to take WeakTypeTag[TC[F]] as additional argument to the macro call and received the paramter type by
val paramT = wfg.tpe.member("apply": TermName).tpe.typeParams.head.tpe
but then using paramT in q"... def apply[$paramT] ..." does result in
java.lang.IllegalArgumentException: can't splice "A" as type parameter
so this appears also to be no solution.
I solved the problem by changing the definition of the above trait to
trait TC[F[_]] {
type ApplyF[A] = F[A]
def apply[A](fa: ApplyF[A]): ApplyF[A]
}
and typechecking the tree for a dummy value:
typecheck(q"""new TC[Foo] {
def apply[A](fa: ApplyF[A]): ApplyF[A] = ???
}""").tpe
The typechecked result then could be destructed and transformed (by means of tree transformers) to fill in the ???. This did not solve the problem completely, yielding the type error:
found : A(in method apply)(in method apply)(in method apply)...
required: A(in method apply)(in method apply)(in method apply)...
While calling untypecheck before returning the tree did not help - inspection of the resulting tree however showed that the expected result is valid (type correct) Scala code. Thus, the last step that made the macro finally fly was to call
parse(showCode(result))
It feels completely unnecessary, but it seems that this was the only way to get rid of conflicting type information.

Can I use nested type in Scala generic function?

How can I use nested type in Scala generic function ? I'd like to implement something like this
implicit def basicDBList2List[List[A]](value : BasicDBList) = value.toList.asInstanceOf[List[A]]
Compiler gives following error:
scala: not found: type A
implicit def basicDBList2List[List[A]](value : BasicDBList) = value.toList.asInstanceOf[List[A]]
^
When you write:
implicit def basicDBList2List[List[A]](value: BasicDBList) = ...
... that doesn't mean what you think it means. You're declaring a new type parameter called List, not referring to the existing List trait in the library! You're also declaring that your newly-defined List type requires some type parameter, which you've called A, but you can't actually reference it.
What you probably meant was:
implicit def basicDBList2List[A](value: BasicDBList): List[A] = ...
... which says that, for any type A, you can convert a BasicDBList to a List[A].
This is sketchy code, though, for two reasons:
What type does your BasicDBList class actually contain? Probably not any possible A. You'll very likely get ClassCastException at runtime.
Why do you want an implicit conversion from BasicDBList to List[A]? That's almost always a bad idea.
I think it is better have it like:
implicit def basicDBList2List[A](value : BasicDBList) = value.toList.asInstanceOf[List[A]]

Spurious ambiguous reference error in Scala 2.7.7 compiler/interpreter?

Can anyone explain the compile error below? Interestingly, if I change the return type of the get() method to String, the code compiles just fine. Note that the thenReturn method has two overloads: a unary method and a varargs method that takes at least one argument. It seems to me that if the invocation is ambiguous here, then it would always be ambiguous.
More importantly, is there any way to resolve the ambiguity?
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
trait Thing {
def get(): java.lang.Object
}
new MockitoSugar {
val t = mock[Thing]
when(t.get()).thenReturn("a")
}
error: ambiguous reference to overloaded definition,
both method thenReturn in trait OngoingStubbing of type
java.lang.Object,java.lang.Object*)org.mockito.stubbing.OngoingStubbing[java.lang.Object]
and method thenReturn in trait OngoingStubbing of type
(java.lang.Object)org.mockito.stubbing.OngoingStubbing[java.lang.Object]
match argument types (java.lang.String)
when(t.get()).thenReturn("a")
Well, it is ambiguous. I suppose Java semantics allow for it, and it might merit a ticket asking for Java semantics to be applied in Scala.
The source of the ambiguitity is this: a vararg parameter may receive any number of arguments, including 0. So, when you write thenReturn("a"), do you mean to call the thenReturn which receives a single argument, or do you mean to call the thenReturn that receives one object plus a vararg, passing 0 arguments to the vararg?
Now, what this kind of thing happens, Scala tries to find which method is "more specific". Anyone interested in the details should look up that in Scala's specification, but here is the explanation of what happens in this particular case:
object t {
def f(x: AnyRef) = 1 // A
def f(x: AnyRef, xs: AnyRef*) = 2 // B
}
if you call f("foo"), both A and B
are applicable. Which one is more
specific?
it is possible to call B with parameters of type (AnyRef), so A is
as specific as B.
it is possible to call A with parameters of type (AnyRef,
Seq[AnyRef]) thanks to tuple
conversion, Tuple2[AnyRef,
Seq[AnyRef]] conforms to AnyRef. So
B is as specific as A. Since both are
as specific as the other, the
reference to f is ambiguous.
As to the "tuple conversion" thing, it is one of the most obscure syntactic sugars of Scala. If you make a call f(a, b), where a and b have types A and B, and there is no f accepting (A, B) but there is an f which accepts (Tuple2(A, B)), then the parameters (a, b) will be converted into a tuple.
For example:
scala> def f(t: Tuple2[Int, Int]) = t._1 + t._2
f: (t: (Int, Int))Int
scala> f(1,2)
res0: Int = 3
Now, there is no tuple conversion going on when thenReturn("a") is called. That is not the problem. The problem is that, given that tuple conversion is possible, neither version of thenReturn is more specific, because any parameter passed to one could be passed to the other as well.
In the specific case of Mockito, it's possible to use the alternate API methods designed for use with void methods:
doReturn("a").when(t).get()
Clunky, but it'll have to do, as Martin et al don't seem likely to compromise Scala in order to support Java's varargs.
Well, I figured out how to resolve the ambiguity (seems kind of obvious in retrospect):
when(t.get()).thenReturn("a", Array[Object](): _*)
As Andreas noted, if the ambiguous method requires a null reference rather than an empty array, you can use something like
v.overloadedMethod(arg0, null.asInstanceOf[Array[Object]]: _*)
to resolve the ambiguity.
If you look at the standard library APIs you'll see this issue handled like this:
def meth(t1: Thing): OtherThing = { ... }
def meth(t1: Thing, t2: Thing, ts: Thing*): OtherThing = { ... }
By doing this, no call (with at least one Thing parameter) is ambiguous without extra fluff like Array[Thing](): _*.
I had a similar problem using Oval (oval.sf.net) trying to call it's validate()-method.
Oval defines 2 validate() methods:
public List<ConstraintViolation> validate(final Object validatedObject)
public List<ConstraintViolation> validate(final Object validatedObject, final String... profiles)
Trying this from Scala:
validator.validate(value)
produces the following compiler-error:
both method validate in class Validator of type (x$1: Any,x$2: <repeated...>[java.lang.String])java.util.List[net.sf.oval.ConstraintViolation]
and method validate in class Validator of type (x$1: Any)java.util.List[net.sf.oval.ConstraintViolation]
match argument types (T)
var violations = validator.validate(entity);
Oval needs the varargs-parameter to be null, not an empty-array, so I finally got it to work with this:
validator.validate(value, null.asInstanceOf[Array[String]]: _*)