implicit conversion for a list of tuple - scala

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))

Related

scala 2.13 auto implicit resolution error

I am facing this weird problem related to scala implicit resolution
Here is the code snippet
import scala.collection.Factory
import scala.collection.immutable.Seq
sealed trait A
sealed trait B
case class BImpl() extends B
case class AImpl() extends A
object implicitsContainer {
type AB = (A, B)
implicit def toStringAnyTuples[C[X] <: Iterable[X], A <: AB]
(col: C[A])
(implicit factory: Factory[(String, Any), C[(String, Any)]])
: C[(String, Any)] = {
factory.fromSpecific(col.iterator.map(f => f._1.toString -> f._2))
}
}
object Main extends App {
import implicitsContainer._
def a(f: Seq[(String, Any)]): Seq[(String, Any)] = f
val w: Seq[(AImpl, BImpl)] = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
// Won't compile
// val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
}
Scala automatically picking up the implicit method
implicit def toStringAnyTuples[C[X] <: Iterable[X], A <: AB](col: C[A])
(implicit factory: Factory[(String, Any), C[(String, Any)]])
: C[(String, Any)] = {
factory.fromSpecific(col.iterator.map(f => f._1.toString -> f._2))
}
for this: -
val w: Seq[(AImpl, BImpl)] = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
but throws an error for this
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
and the error is:-
Error:(44, 47) type mismatch;
found : (AImpl, BImpl)
required: (String, Any)
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
and one more point, if I remove the type from w
val w = Seq(AImpl() -> BImpl())
val x: Seq[(String, Any)] = a(w)
then also this will work fine.
The only error is with
val y: Seq[(String, Any)] = a(Seq(AImpl() -> BImpl()))
I am using: -
SCALA -> 2.13.3
SBT -> 1.3.13
JAVA -> 14
It's just type inference issue. Type parameter of Seq.apply was not inferred. Try
val y: Seq[(String, Any)] = a(Seq[(AImpl, BImpl)](AImpl() -> BImpl()))
or
val y: Seq[(String, Any)] = a(Seq[(A, B)](AImpl() -> BImpl()))
What you are experiencing is a symptom of the way inference works in the Scala compiler.
Here is a smaller example that shows the same issue:
object Test {
class C[T](x: T)
implicit def conv(c: C[Int]): C[String] = ???
def m(cs: C[String]) = 1
val ci = new C(1)
def t1: Int = m(ci) // OK
def t2: Int = m(new C(1)) // found: Int, expected: String
}
When type-checking new C(1), the compiler pushes down the expected type String to type
checking the expression 1, which fails. In the line above, type checking ci with expected type
C[String] succeeds thanks to the implicit conversion.
My suggestion here would be to define an extension method that performs the conversion, instead
of making the conversion implicit. This is also recommended for clarity - implicit conversions like
the one defined in your example can lead to surprising, hard to diagnose issues.
In my example, it would look like this:
object Test {
class C[T](x: T)
implicit class CExt(private val c: C[Int]) extends AnyVal {
def toCString: C[String] = ???
}
def m(cs: C[String]) = 1
val ci = new C(1)
def t1: Int = m(ci.toCString)
def t2: Int = m(new C(1).toCString)
}

scala function with implicit parameters

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.

Applying implicit conversion to map

I tried implicit conversions in the following example:
val m: Map[Int, Int] = Map(10 -> "asd") //fine
val mm: Map[Int, Int] = Map("asd" -> 20) //type mismatch; found: (String, Int)
//required: (Int, Int)
implicit def stringToInt(str: String): Int = 10
Why can't we apply implicit conversions to map keys? Is there a way to work around this?
It doesn't work because you're using -> which is an (inline) operator:
implicit final class ArrowAssoc[A](self : A) extends scala.AnyVal {
#scala.inline
def ->[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
def →[B](y : B) : scala.Tuple2[A, B] = { /* compiled code */ }
}
You can see that by the time B is evaluated, A is already "fixed". Let's just say that you can only (implicitly) convert the right hand side of a tuple when using -> operator:
implicit def stringToInt(str: String): Int = 10
implicit def intToStr(str: Int): String = "a"
val a: Map[Int, Int] = Map(10 -> "asd") //fine
val b: Map[Int, Int] = Map("asd" -> 20) // error! cant change left side
val c: Map[String, String] = Map("asd" -> 20) // fine
val d: Map[String, String] = Map(10 -> "asd") // error! cant change left side
Because of similar compiler quirks related to using operator ->, #Jorg's solution works in one direction, but not the other:
implicit def tupleIntifier(t: (String, Int)) = (10, 10)
implicit def tupleIntifier2(t: (Int, String)) = (10, 10)
val a: Map[Int, Int] = Map("asd" -> 20) // uses tupleIntifier
val b: Map[Int, Int] = Map(10 -> "asd") // fails!!
However, if you avoid using -> operator altogether and simply use (key, value) syntax, it will work:
val a: Map[Int, Int] = Map((10, "asd"))
val b: Map[Int, Int] = Map(("asd", 20))
implicit def stringToInt(str: String): Int = 15
println(a) // prints Map(10 -> 15)
println(b) // prints Map(15 -> 20)
Please, look at the error message you are getting:
error: type mismatch;
found : (String, Int)
required: (Int, Int)
val mm: Map[Int, Int] = Map("asd" -> 20)
^
The error message is not about String instead of Int, it is about (String, Int) instead of (Int, Int). So, you are simply converting the wrong thing:
implicit def tupleIntifier[T](t: (String, T)) = (10, t._2)
val mm: Map[Int, Int] = Map("asd" -> 20)
//=> mm: Map[Int,Int] = Map(10 -> 20)
Voila! It works.
If you were to add such a general implicit conversion, you would lose the type-safety that Scala is enforcing, because any String would become an Int as needed, anywhere, without intervention from the programmer.
In reality, when you want to create that map from other data, you probably already know the data types of that other data. So if the keys are known to be integers, convert them to Int and use them like that. Otherwise, use strings.
Your example is highly artificial. Which concrete problem are you trying to solve?

Type of "foreach" in Scala

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.

Diverging implicit expansion for type shapeless.Typeable[...]

I have a value of type Any. If its class is Map[String, Any] I must be able to cast it to a case class and for this purpose I'm using the solution proposed by Travis Brown here. The problem is that when I define a Typeable implementation as the following, in certain cases, I get the diverging implicit expansion error:
implicit def caseClassTypeable[T, R <: HList](implicit castT: Typeable[T],
gen: LabelledGeneric.Aux[T, R],
fromMap: FromMap[R]) =
new Typeable[T] {
override def cast(t: Any): Option[T] = t.cast[Map[String, Any]] flatMap (m => to[T].from(m))
override def describe: String = castT.describe
}
REPL session:
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> case class Simple(foo: String, bar: Int)
defined class Simple
scala> val m: Any = Map("foo" -> "a", "bar" -> 42)
m: Any = Map(foo -> a, bar -> 42)
scala> m.cast[Simple]
res7: Option[Simple] = Some(Simple(a,42))
scala> val l: Any = List(1, 2, 3)
l: Any = List(1, 2, 3)
scala> l.cast[List[Any]]
<console>:22: error: diverging implicit expansion for type shapeless.Typeable[List[Any]]
starting with method inrTypeable in object Typeable
l.cast[List[Any]]
^
However this one works:
scala> l.cast[List[Int]]
res9: Option[List[Int]] = Some(List(1, 2, 3))
Any idea?