I'm improving the Scala support in Querydsl and I encountered the following issue. Here is a code snippet that illustrates the problem :
// framework types
class RelationalPath[T]
class RelationalPathAdapter[T, R <: RelationalPath[T]](p: R) { def someMethod = {} }
// domain types
class User
class QUser extends RelationalPath[User]
implicit def toAdapter[T, R <: RelationalPath[T]](p: R) = new RelationalPathAdapter[T,R](p)
val path = new QUser()
toAdapter(path).someMethod // DOESN'T COMPILE
path.someMethod // DOESN'T COMPILE
How to match a type in a implicit conversion in addition to it's type argument. I can match either separately, but not both.
In this specific case, the following change works:
implicit def toAdapter[T, R <: RelationalPath[T]](p: RelationalPath[T] with R) =
new RelationalPathAdapter[T,R](p)
This is not really an implicit conversion issue, rather a type inference issue. When you call toAdapter(path), there is nothing implicit.
If you pass the type parameter, it works.
toAdapter[User, QUser](path).someMethod
It can even work with an implicit conversion :
(path: RelationalPathAdapter[User, QUser]).someMethod
but of course, this is rather useless.
The proper way to write the implicit is
implicit def toAdapter[T, R[X] <: RelationalPath[X]](p: R[T])
= new RelationalPathAdapter[T, R[T]](p)
Related
I have some scala 2.13 code that basically boils down to this
import scala.language.implicitConversions
trait Base {
type V
def v: V
}
case class Derived(v: Int) extends Base {
type V = Int
}
object TestImplicitConversion {
implicit def getV[T <: Base](a: T): T#V = a.v
val a: Int = Derived(5)
}
Here I would expect the compiler to use the implicit conversion getV to convert Derived to Int, but the code does not compile. Manually adding the call to getV, will make the code compile. Can someone help me understand why the conversion where in the standard it is explained.
The way I found of making such a conversion work is by adding a second generic parameter and a constraint
implicit def getV2[T <: Base, S](a: T)(implicit constraint: T#V =:= S): S = constraint(a.v)
with this version the compiler uses the conversion and the code does compile.
Edit:
The alternative solution provided by #user using refinement type does indeed seem like a better approach. But it does not really provide an answer to why it original implementation does not work. So I am still interested in understanding why the compiler does not use the implicit def when an explicit call will make the code compile.
As gianluca aguzzi mentioned in the comments, type projection is unsound and should be avoided. Moreover, T is not a concrete type, so you can't use projections on it anyway. You can accept only Base#V as a type parameter instead, and use a refinement type for the type of a:
implicit def get[T](a: Base { type V = T }): T = a.v
Thus, you can avoid casting and type projections.
Scastie
Having this type class for converting a Map into a case class:
/**
* Type class for transforming a map of values into a case class instance.
*
* #tparam T Wanted case class type.
*/
#implicitNotFound("Missing ToCaseClassMapper implementation for type ${T}")
trait ToCaseClassMapper[T <: Product] {
def toCaseClass(map: Map[String, Any]): T
}
And this function to implicitly get the correct mapper
def toCaseClass[T <: Product](map: Map[String, Any])(implicit mapper: ToCaseClassMapper[T]): T = {
mapper.toCaseClass(map)
}
It can be used as toCaseClass[User](aUserMap) // returns User
But I also would like to be able to use this function with an Option[Map[]] or Future[Map[]] or List[Map[]].
So I implemented a generic function using a functor like this:
def toCaseClass[T <: Product, F[_]: cats.Functor](fOfMaps: F[Map[String, Any]])(implicit mapper: ToCaseClassMapper[T]): F[T] = {
cats.Functor[F].map(fOfMaps)(map => toCaseClass(map)(mapper))
}
But now this function has to be used as toCaseClass[User,List](listOfUserMaps) // returns List[User].
However, I'd would like to be able to use the function as
toCaseClass[User](listOfMaps)
toCaseClass[User](futureOfMap)
toCaseClass[User](optionOfMap)
without the need to specify the functor type.
Is this somehow possible?
Could Shapeless's Lazy be used to solve this?
Edit: solution
Thanks to #Jasper-m and #dk14 for their answers.
So the 'trick' to solve this is to capture the type 'T' first in a class before the Functor type. I liked #Jasper-m solution with the 'apply' method since that would keep the syntax almost similar to what it was before.
I made a few adjustments though. Since there was already the 'ToCaseClassMapper' class which also captures the type 'T', I decided to combine it with the 'ToCaseClass' class. Also, with #Jasper-m's approach, when using the 'toCaseClass' function when mapping over some value like Option(value).map(toCaseClass) the usage of toCaseClass had to be different for when the value was a Map or a List[Map].
My solution is now as follows:
#implicitNotFound("Missing ToCaseClassMapper implementation for type ${T}")
trait ToCaseClassMapper[T <: Product] {
def toCaseClass(map: Map[String, Any]): T
import scala.language.higherKinds
def toCaseClass[F[_]: cats.Functor, A](fOfMaps: F[Map[String, A]]): F[T] = {
cats.Functor[F].map(fOfMaps)(toCaseClass)
}
}
Since the ToCaseClassMapper instance was already implicitly available where the toCaseClass function was used, I decided to throw away that function and just replace it with mapper.toCaseClass(_). This cleaned up some unneeded code and now the syntax for using the mapper is the same regardless whether the value is a Map or Option, List, Future (or any other Functor).
Currently it's not possible in Scala to have one type parameter provided explicitly and another one in the same type parameter list be inferred, nor is it currently possible to have multiple type parameter lists for a method. A workaround is to create a helper class and split your method call in two stages: first create an instance of the helper class, then call the apply method on that object.
class ToCaseClass[T <: Product] {
def apply[F[_]: cats.Functor, A](fOfMaps: F[Map[String, A]])(implicit mapper: ToCaseClassMapper[T]): F[T] = {
cats.Functor[F].map(fOfMaps)(map => toCaseClass(map)(mapper))
}
}
def toCaseClass[T <: Product] = new ToCaseClass[T]
def toCaseClass[T <: Product](map: Map[String, Any])(implicit mapper: ToCaseClassMapper[T]): T = {
mapper.toCaseClass(map)
}
toCaseClass[User](listOfMaps)
toCaseClass[User](futureOfMap)
toCaseClass[User](optionOfMap)
Edit: As pointed out by dk14, there is still a type inference problem here, where F is inferred as Any. I don't know what causes it, but I think it is a separate orthogonal problem from the one being solved by this pattern.
Edit 2: I figured it out. It's because F is invariant in its type parameter. F[Map[String, String]] is not a subtype of F[Map[String, Any]], so the compiler does something strange and infers F as Any. A solution is to put a type parameter A instead of Any, or use an existential type Map[String,_].
This works:
class Mapper[T <: Product](implicit val mapper: ToCaseClassMapper[T]){
def toCaseClass[F[_]: cats.Functor, Z <: Map[String, Any]](fOfMaps: F[Z]): F[T] = {
cats.Functor[F].map(fOfMaps)(map => mapper.toCaseClass(map))
}
}
object Mapper{
def apply[T <: Product: ToCaseClassMapper] = new Mapper[T]{}
}
import cats.implicits._
Mapper[User].toCaseClass(List(Map("aaa" -> 0)))
Few tricks besides obvious introducing class (to split type parameters) were used as well:
1) move mapper to constructor so it could be resolved first (not sure it helped)
2) what definitely helped is to introduce Z <: Map[String, Any], otherwise scala (at least my old version 2.11.8) would infer F[_] as Any for some reason
P.S. You can use apply instead of toCaseClass as well - it would shorten the syntax
While this problem was caught in code using shapeless and kind-projector, this behavior could be reproduced without them.
Suppose I have simple typeclass for reifying typeclass instances with incomplete implementation (mirror of LiftAll).
sealed trait LiftAll1[F[_], In] {
type Out
def instances: Out
}
object LiftAll1 {
type Aux[F[_], In0, Out0] = LiftAll1[F, In0] {type Out = Out0}
implicit def unit[F[_]]: Aux[F, Unit, Unit] = new LiftAll1[F, Unit] {
type Out = Unit
def instances = Unit
}
}
And some very simple type class to test it
sealed class FirstIs[M, T]
object FirstIs {
implicit def firstIs[M, D]: FirstIs[M, (M, D)] = new FirstIs
}
Things are ok if I'll try to apply FirstIs partially via alias, and get instance via LiftAll1
type FirstIsInt[D] = FirstIs[Int, D]
implicitly[LiftAll1[FirstIsInt, Unit]]
But inlined partial type application leads to compilation error
implicitly[LiftAll1[({type lambda[x] = FirstIs[Int, x]})#lambda, Unit]]
//Error: could not find implicit value for parameter e: LiftAll1[[x]FirstIs[Int,x],Unit]
How partially applied typeclasses could be found in such situations?
As #Reactormonk suggested, the compiler was brought to its senses with following line in the build.sbt
scalacOptions += "-Ypartial-unification"
After that original piece of code, which is close to
import shapeless._, ops.hlist._
LiftAll[FirstIs[Int, ?], HNil]
was succesfully compiled
AFAIK problem was in scala compiler incapability to understand FirstIs[Int,_] as F[_]in supplied implicit def without direct type alias . Fortunately this was fixed in latest scala implementations.
Scala 2.8.1
Take the following class hierarchy
abstract class A
class B extends A
class C extends A
Why is the scala compiler unable to find the implicit parameter for send when sending an instance of B below
implicit def routingKeyFor[T <: A](value: T) =
value.getClass.getSimpleName
implicit def routingKeyFor(value: C) = "custom C"
def send[T <: A](value: T)(implicit createRoutingKey: T => String):
Validation[Throwable, String] = Success(createRoutingKey(value))
val resultOfSendingB = send(new B)
val resultOfSendingC = send(new C)
Why is the compiler able to locate the value for the implicit parameter when the generic version of routingKeyFor is renamed?
implicit def someOtherName[T <: A](value: T) =
value.getClass.getSimpleName
The second implicit is shadowing the first one. Why is anyone's guess, and you might open an issue for it (after verifying that this wasn't reported before), but it might just be one of those things that throw a spanner into the works of type inference.
I have this bit of code, which works:
val directions = rs.map(_.direction) // Direction extends Enumeration
directions == directions.sorted.reverse
I'd like to instead do something like this:
ratings.map(_.direction).isInBackwardsOrder
class RichSeq[T](seq: Seq[T]) {
def isInBackwardsOrder = seq == seq.sorted.reverse
}
object RichSeq {
implicit def seq2richSeq[T](seq: Seq[T]) = new RichSeq[T](seq)
}
I keep getting the following compilation error:
could not find implicit value for parameter ord: Ordering[T]
def isInBackwardsOrder = seq == seq.sorted.reverse
What I don't understand is why it could find the implicit value for parameter ord, when it was in the original form, but cannot find it once I pull it into a utility class.
Thanks for the help,
Alex
In the original form, you had no generics. directions is a Seq[SomeWellKnownType], and at compile time, the compiler looks for an Ordering[SomeWellKnownType] in implicit scope, and finds one.
On the other hand, in RichSeq[T], the compiler must find an implicit Ordering[T] where T is a type parameter. No way to do that. You must ensure that the Ordering will be available when you create the RichSeq :
class RichSeq[T](seq: Seq[T])(implicit ev: Ordering[T]) {...
There is a shortcut for that, especially if you just need ev in implicit scope without refrencing it explicitly, as in here :
class RichSeq[T : Ordering](seq: Seq[T]) {...
Then you have the exact same problem in your implicit method, which is generic too, with the same solution :
implicit def seq2richSeq[T: Ordering](seq: Seq[T]) = new RichSeq[T](seq)
Then it should work. The seq2richSeq implicit conversion will kick in when an Ordering is available for the type of the elements in the Seq.