I am trying to create a mock for Play's WSClient like this:
def mockGet[A](url : String, method : String, headers : Seq[(String, String)], timeout : Duration)(
response: Future[AhcWSResponse]
) =
(mockWsClient
.url(_ : String)
.withMethod(_ : String)
.withHttpHeaders(_: (String, String)*)
.withRequestTimeout(_ : Duration)
.stream())
.expects(url, method, headers, timeout)
.returning(response)
The problem is the withHttpHeaders - this actually takes (String, String)* but when I specify that type as above I get a compiler error like this:
[error] found : Seq[(String, String)]
[error] required: (String, String)
[error] .withHttpHeaders(_: Seq[(String, String)])
What type do I need to specify for this method because (String, String) is not correct. The actual real definition of this method is:
override def withHttpHeaders(headers: (String, String)*): Self
UPDATE
I tried this after #Mario's suggestion:
def mockGet[A](url: String, method: String, headers: Seq[(String, String)], timeout: Duration)(
response: (String, String, Duration) => Future[ws.WSResponse]
) =
(
(
xs: Seq[(String, String)]
) =>
mockWsClient
.url(_: String)
.withMethod(_: String)
.withRequestTimeout(_: Duration)
.withHttpHeaders(xs: _*)
.stream()
)
.expects(headers)
.returning(response)
but this crashes the compiler with:
[error] value x$1
The key is to understand how the anonymous function placeholder parameter syntax works. For example, given
def f(i: Int*) = ???
then
f(_: Int)
expands to
(i: Int) => f(i)
Hence try
def mockGet(headers : Seq[(String, String)) =
((xs: Seq[(String, String)]) => mockWsClient.withHttpHeaders(xs: _*)).expects(headers)
Here is a simplified example
trait Zar {
def f(i: Int*) = i
}
class ScalamockVarargsSpec extends FlatSpec with Matchers with MockFactory {
"Varargs" should "be mockable" in {
val zar = mock[Zar]
((xs: Seq[Int]) => zar.f(xs: _*)).expects(Seq(1,2))
zar.f(1,2)
}
}
In your particular case there are multiple anonymous function placeholder parameters, so try expanding them all, for example
def mockGet(url: String, headers : Seq[(String, String)) =
((u: String, xs: Seq[(String, String)]) => mockWsClient.url(u).withHttpHeaders(xs: _*))
.expects(url, headers)
Related
I am trying to convert implicitly a List[(Int, String)] to List[(IntWrap, String)], which is giving an error of TypeMismatch.
I tried few other conversion which works which are
List[Int] to List[IntWrap] and Tuple2[Int, String] to Tuple2[IntWrap, String] which works.
case class IntWrap(a : Int)
implicit def int2IntWrap(x: Int) = IntWrap(x)
implicit def int2IntWrapForTuple2(tuple: Tuple2[Int, String]) = (IntWrap(tuple._1), tuple._2)
//Defined few methods
def foo(t : (IntWrap, String)) = println(t._2)
def foo_list(t: List[IntWrap]) = println(t.size)
def foo_list_tuple(t : List[(IntWrap, String)]) = println(t.size)
foo(3 -> "hello") //this works
foo_list(List(1, 2, 3)) //this works
val l : List[(IntWrap, String)] = List(3 -> "hello")
foo_list_tuple(l) //this works
val l1 = List(3 -> "hello")
foo_list_tuple(l1) //this one doesn't work
//error: type mismatch; found: List[(Int, String)] required: List[(IntWrap, String)]
How can I make this work?
Try defining implicit conversion like so
implicit def listOfTupleToListOfIntWrapTuple(l: List[(Int, String)]): List[(IntWrap, String)] =
l.map(tuple => (IntWrap(tuple._1), tuple._2))
I have scala function as below,
scala> def getOrders: (String, String) => Seq[String] = (user: String, apiToken: String) => Seq.empty[String]
def getOrders: (String, String) => Seq[String]
scala> getOrders("prayagupd", "A1B2C3")
val res0: Seq[String] = List()
I want to pass in a third parameter as a implicit parameter but it does not seem possible for a function.
Here's what I want achieved using a method,
scala> def getOrders(user: String, apiToken: String)(implicit clientType: String) = Seq.empty[String]
def getOrders
(user: String, apiToken: String)(implicit clientType: String): Seq[String]
scala> implicit val clientType: String = "android"
implicit val clientType: String = "android"
scala> getOrders("prayagupd", "A1B2C3")
val res2: Seq[String] = List()
It does not seem possible because of the fact that apply function is predefined, which won't extra accept implicit parameter.
scala> new Function2[String, String, Seq[String]] {
def apply(user: String, apiToken: String): Seq[String] = Seq.empty
}
val res4: (String, String) => Seq[String] = <function2>
Overloadding does not do the trick either,
scala> new Function2[String, String, Seq[String]] {
def apply(user: String, apiToken: String): Seq[String] = Seq.empty
def apply(user: String, apiToken: String)(implicit clientType: String) = Seq("order1")
}
val res9: (String, String) => Seq[String] = <function2>
scala> implicit val clientType: String = "device"
implicit val clientType: String = "device"
scala> res9("prayagupd", "apiToken")
val res10: Seq[String] = List()
Is it that implicits are not recommended at all for functions or I'm missing something?
Experimental, your function might be expressed as follows without the implicit:
scala> def getOrders: (String, String) => (String) => Seq[String] = (user: String, apiToken: String) => (clientType: String) => Seq.empty[String]
def getOrders: (String, String) => String => Seq[String]
Poking around on that... it doesn't like implicit anywhere in there that might give you want you want.
An answer to a related question suggests the reason: getOrders "... is a method, not a function, and eta-expansion (which converts methods to functions) is not attempted until after implicit application." It seems that implicits are resolved at a method level, not a function level.
I have example code:
def test = {
val l : Seq[(String, String)] = Seq()
val foo : (String, String) => Unit = {case (a, b)=>}
l.foreach[Unit](foo)
}
It gives me the next error:
Error:(8, 21) type mismatch;
found : (String, String) => Unit
required: ((String, String)) => Unit
l.foreach[Unit](foo)
As far as I understand foreach has a type (A=>U)=>Unit where:
A is template parameter of my collection and
U is template parameter of foreach itself
My question is why is ((String, String)) => Unit required? Where do the extra brackets come from?
If you check in the REPL, you'll see that foo is a <function2>, that is, a function that takes 2 argument:
scala> val foo : (String, String) => Unit = {case (a, b)=>}
foo: (String, String) => Unit = <function2>
.foreach however expects a function that takes a single arguement (of type A), which in your case is a Tuple2.
If you set foo to be a <function1>, then it works:
scala> val foo : ((String, String)) => Unit = {case (a, b)=>}
foo: ((String, String)) => Unit = <function1>
scala> val l : Seq[(String, String)] = Seq()
l: Seq[(String, String)] = List()
scala> l.foreach[Unit](foo)
scala>
Let's start from top.
You have
val l: Seq[(String, String)]
that is a Seq of tuples of two strings. So each of the elements of l is of type (String, String) which is a syntactic sugar for Tuple2[String, String].
Now you have
val foo: (String, String) => Unit
that is a function of two arguments each being String. Again, the type signature above is syntactic sugar for Function2[String, String, Unit].
Considering the above your code can be rewritten as follows:
def test = {
val l: Seq[Tuple2[String, String]] = Seq()
val foo: Function2[String, String, Unit] = {case (a, b)=>}
l.foreach[Unit](foo)
}
foreach expects a single argument function, i.e. a Function1[T, R], thus the type mismatch.
The type definition foo: (String, String) => Unit says that foo is a function which takes two parameters of type String. However, your sequence l: Seq[(String, String)] contains tuples of type type t = (String, String). Thus, calling l.foreach expects a function which is applicable to the tuple type t. Thus, it expects a function of type t => Unit, which is equivalent to ((String, String)) => Unit.
Using Spark 1.3.0 with Scala, I have two functions which basically do the same on a given RDD[(Long, String, Boolean, String)], up to a specifc map function from (Long, String, Boolean, String) to a tuple of 2 elements:
def rddToMap1(rdd: RDD[(Long, String, Boolean, String)]): Map[Long, Set[(String, Boolean)]] = {
rdd
.map(t => (t._1, (t._2, t._3))) //mapping function 1
.groupBy(_._1)
.mapValues(_.toSet)
.collect
.toMap
.mapValues(_.map(_._2))
.map(identity)
}
def rddToMap2(rdd: RDD[(Long, String, Boolean, String)]): Map[(Long, String), Set[String]] = {
rdd
.map(t => ((t._1, t._2), t._4)) //mapping function 2
.groupBy(_._1)
.mapValues(_.toSet)
.collect
.toMap
.mapValues(_.map(_._2))
.map(identity)
}
I want to write a generic function genericRDDToMap which I would later use to implement rddToMap1 and rddToMap2.
This doesn't work:
def genericRDDToMap[A](rdd: RDD[(Long, String, Boolean, String)], mapFn: (Long, String, Boolean, String) => A) = {
rdd
.map(mapFn) //ERROR
.groupBy(_._1)
.mapValues(_.toSet)
.collect
.toMap
.mapValues(_.map(_._2))
.map(identity)
}
The (Eclipse) interpreter doesn't take mapFn as a valid mapping function, it says:
type mismatch; found : (Long, String, Boolean, String) => A required: ((Long, String, Boolean, String)) => ?
And even if I got over this, how would it know that my generic type A has value _1 in the groupBy to follow?
To summarize: how do I do it right?
You missed parentheses around (Long, String, Boolean, String). And if A is of type TupleX, you can use upper bound to specify it (here I used Tuple2):
def genericRDDToMap[X, Y, A <: Tuple2[X,Y]](rdd: RDD[(Long, String, Boolean, String)],
mapFn: ((Long, String, Boolean, String)) => A) (implicit ev: ClassTag[A])= {
...
}
I've defined an enum
object SupportedCurrencies extends Enumeration {
type SupportedCurrencies = Value
val USD, GBP, ARS, AUD, BRL, CAD, CHF, CNY, EUR, JPY, SEK, DKK, NOK = Value
}
and implicit conversion for it to string
implicit def supportedCurrencyToString(currency: SupportedCurrencies.Value): String = currency.toString
but if I'll try to create a Map[String, Int]
val m: Map[String, Int] = Map(USD -> 1)
I'm getting an error
type mismatch;
[error] found : (helpers.SupportedCurrencies.Value, Int)
[error] required: (String, Int)
[error] val m: Map[String, Int] = Map(USD -> 1)
Could anyone explain what's wrong here?
I looked closely at the error and get it
implicit def supportedCurrencyToString(currencyEntry: (helpers.SupportedCurrencies.Value, Int)): (String, Int) = currencyEntry._1.toString -> currencyEntry._2
I was needed conversion from pair to pair