Scala mock function with implicit generic types - scala

I'm trying to mock Cassandra ScalaGettableData object using scalamock. I need to mock the following method:
def getMap[K : TypeConverter, V : TypeConverter](name: String) = get[Map[K, V]](name)
TypeConverter is a Trait and has implicit implementations such as:
implicit object StringConverter extends TypeConverter[String]
In my code I'm calling
scalaGettableData.getMap[String, String]("myMap")
and I guess it's implicitly converted to
scalaGettableData.getMap[StringConverter, StringConverter]("myMap")
My Test code is as following :
val cassandraRow1 = mock[ScalaGettableData]
(cassandraRow1.getMap[String, String] _).expects("localizations_config").returning(Map("key1" -> "value1"))`
But I'm getting compilation error:
Error:(28, 26) _ must follow method; cannot follow (name: String)(implicit evidence$3: com.datastax.spark.connector.types.TypeConverter[String], implicit evidence$4: com.datastax.spark.connector.types.TypeConverter[String])Map[String,String] <and> (index: Int)(implicit evidence$3: com.datastax.spark.connector.types.TypeConverter[String], implicit evidence$4: com.datastax.spark.connector.types.TypeConverter[String])Map[String,String]
How am I supposed to mock this method ?

Maybe this example helps:
"implicit parameters" should "be mockable" in {
trait Bar[T]
trait Foo {
def getMap[K : Bar](name: String): Int
}
val m = mock[Foo]
(m.getMap[Long](_: String)(_: Bar[Long])) expects(*, *) returning 42 once()
implicit val b = new Bar[Long] {}
m.getMap("bar")
}
Effectively, the type parameter K : Bar gets converted by the Scala compiler to a second parameter list, which is mocked out explicitely in this example with (_: Bar[Long]).

Related

Scala implicit conversion for type aliases

Define
type TA[T] = T => Int
implicit class TAOps[T](a: TA[T]) {
def foo(): Unit = {println("TA")}
}
val ta: TA[Double] = x => x.toInt
Now,
ta.foo()
fails to compile with the message value foo is not a member of ammonite.$sess.cmd1.TA[Double],
while the explicit call
TAOps(ta).foo()
prints TA. Why does the implicit conversion not work in the former case?
Your implicit def is expecting a type that receives one type parameter, i.e. a TA[T]
Your declaration: val ta: TA[Double] = ... is a type it self, and doesn't take any type parameters. So the compiler will not use your implicit def to type check this.
Conclusion you have an implicit type conversion for a type that takes a type parameter and TA[Double] doesn't take any type parameters.
Solutions:
1 - Replace the implicit type conversion to receive a Function1:
implicit class TAOps[T](a: T => Int) {
def foo: Unit = {
println("TA")
}
}
2 - Use type lambdas:
implicit class TAOps[T](a: ({type Alias = TA[T]})#Alias) {
def foo: Unit = {
println("TA")
}
}
Here the type you created is curried. So the compiler will now apply this implicit conversions to types that match, it is no longer expecting a type that receives 1 type parameter.
More on Type Lambdas

Scala ClassTag & classOf and type parameter. Strange case of generics

Here is my code straight & simple:
package scalaproj
import scala.reflect._
case class MyClass() {}
def bar[T](cls : Class[T]) = println(cls)
def foobar[T: ClassTag] = println(classTag[T])
bar(classOf[MyClass])
foobar[MyClass]
Results: class scalaproj.GetFields$MyClass$2
scalaproj.GetFields$MyClass$2
Now I would like to do the following without the famous error: "class type required but T found"
def foo[T] = println(classOf[T])
foo[MyClass]
foo is just a function that takes a Generic Type Parameter and does not need a value parameter. I think this is strange given the two examples that work and all the flexibility build in into the Scala language and its handeling of generics.
Update:
Just to specify further strangeness:
def foo1[T](t : T) = {} // no compile error
def foo2[T](): List[T] = { List[T]() } // no compile error
def foo3[T](): T = { T() } // compile error: "not found: value T"
A good explanation is appreciated.
You can't, as classOf will not work with arbitrary types (and your T is an arbitrary type).
For example:
scala> classOf[Int with String]
<console>:15: error: class type required but Int with String found
classOf[Int with String]
^
You can achieve the same thing with ClassTag#runtimeClass:
def foo[T: ClassTag] = println(classTag[T].runtimeClass)

Scala compiler says "No TypeTag available for T" in method using generics

The following code is not compiling:
override def read[T <: Product](collection : String): Dataset[T] = {
val mongoDbRdd = MongoSpark.load(sparkSession.sparkContext,MongoDBConfiguration.toReadConfig(collection))
mongoDbRdd.toDS[T]
}
This is "toDS" definition:
def toDS[T <: Product: TypeTag: NotNothing](): Dataset[T] = mongoSpark.toDS[T]()
Compiler says:
Error:(11, 20) No TypeTag available for T
mongoDbRdd.toDS[T]
Error:(11, 20) not enough arguments for method toDS: (implicit evidence$3: reflect.runtime.universe.TypeTag[T], implicit evidence$4: com.mongodb.spark.NotNothing[T])org.apache.spark.sql.Dataset[T].
Unspecified value parameters evidence$3, evidence$4.
mongoDbRdd.toDS[T]
Line 11 is mongoDbRdd.toDS[T]
I really don't know what's going on with Scala Generics and the compiler is not very specific, any idea?
The problem is with the type constraints on T that toDS requires:
// The ':' constraint is a type class constraint.
def toDS[T <: Product: TypeTag: NotNothing](): Dataset[T] =
mongoSpark.toDS[T]()
// The below is exactly the same as the above, although with user-defined
// names for the implicit parameters.
// All a type class does is append implicit parameters to your function.
def toDS[T <: Product]()(implicit typeTag: TypeTag[T], notNothing: NotNothing[T]) =
mongoSpark.toDS[T]()
You'll notice that's what your compiler error shows - with the names expanded to evidence$3 and evidence$4.
If you want your method to compile, simply add the same type classes:
override def read[T <: Product: TypeTag: NotNothing](
collection : String): Dataset[T] = { /* impl */ }

Making Typeclass Instance with `implicit def`?

Given:
scala> trait Resource[A] { def f: String }
defined trait Resource
scala> case class Foo(x: String)
defined class Foo
And then an implicit:
scala> implicit def fooToResource(foo: Foo): Resource[Foo] =
new Resource[Foo] { def f = foo.x }
The following works:
scala> implicitly[Resource[Foo]](Foo("foo")).f
res2: String = foo
I defined a function:
scala> def f[A](x: A)(implicit ev: Resource[A]): String = ev.f
f: [A](x: A)(implicit ev: Resource[A])String
However, the following code fails to compile:
scala> f(Foo("foo"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
f(Foo("foo"))
Secondly, then I tried:
scala> f2(Foo("bippy"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
f2(Foo("bippy"))
^
Lastly, I attempted:
scala> def g(foo: Foo)(implicit ev: Resource[Foo]): String = ev.f
g: (foo: Foo)(implicit ev: Resource[Foo])String
scala> g(Foo("5"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
g(Foo("5"))
^
However, it failed too. How can I fix f?
Ok with Peter Neyens' answer, this is not a typeclass, this is an implicit conversion, which you should avoid - there should have been some warning, asking that you import scala.language.implicitConversions.
As a complement, here is why the first implicitly works:
Implicitly is just:
def implicitly[T](implicit ev: T): T = e
When you write implicitly[T] without supplying a parameter, it will look for an implicit of type T in scope and return it. However, you call implicitly with a parameter (I believe there is no legitimate reason to do that, ever), so it would just return your parameter, Foo("foo"), an instance of Foo. Except that you explicitly stated that T should be Resource[Foo]. If you had written a type ascription, such as (Foo("foo"): Resource[Foo]), it would have worked the same way. implicitly is not relevant here.
The point is that Foo("foo") is not of the expected type Resource[Foo], but just a Foo. The compiler would reject that, except that at this point, the implicit conversion you defined above kicks in, and your Foo instance is transformed into a Resource[Foo]. Then, you can call f.
Next, you call your f(Foo("foo")). There is an implicit parameter, however this time, you don't supply it. So the compiler looks for one (while it did no such thing the first time), and as there is no such instance, fails.
The implicit def fooToResource is not a type class instance, but does return one if you supply a Foo, that's the reason the following line works :
implicitly[Resource[Foo]](Foo("foo")).f
A solution would be to change the Resource.f function to take a parameter of type A :
trait Resource[A] {
def f(a: A): String
}
You then could define a Resource type class instance for Foo as follows:
case class Foo(x: String)
implicit val fooResource = new Resource[Foo] {
def f(foo: Foo) = foo.x
}
We can rewrite f to use the changed Resource :
def f[A](a: A)(implicit resA: Resource[A]): String = resA.f(a)
Which does what (I think) you need :
f(Foo("hello world")) // String = hello world

Implicit not found when omitting empty argument list

I have the following (simplified) code:
case class Value[T](value: T)
trait Absable[In,Out] {
def absoluteValue(in: In): Out
}
implicit class AbsValue[In, Out](in: Value[In]) {
def abs()(implicit ev: Absable[In, Out]): Value[Out] = Value(ev.absoluteValue(in.value))
}
implicit def AbsNumeric[A : Numeric] = new Absable[A, A] {
def absoluteValue(in: A) = implicitly[Numeric[A]].abs(in)
}
Now I want to use the abs function on a Value:
scala> Value(-3).abs()
res3: Value[Int] = Value(3)
scala> Value(-3).abs
<console>:14: error: could not find implicit value for parameter ev: Absable[Int,Nothing]
Value(-3).abs
^
I added an empty argument list in front of the implicit arguments to give callers more flexibility, but now when I omit the empty list at the call site the compiler can't find the implicit... So now instead of more flexibility callers get confusing compile errors.
I don't understand how leaving off the argument list can affect the type inference or implicit resolution.
I am using scala 2.11.6