I would like to perfom two maps over shapeless HList, I have folllowing code that works:
object doubleToInt extends Poly1 {
implicit def caseDouble = at[Double](_.toInt)
}
object intToDouble extends Poly1 {
implicit def caseInt = at[Int](_.toDouble)
}
def convert[InputType<:HList, ResultType<:HList, IntermediateType<:HList](input: InputType)(
implicit mapper1: Mapper.Aux[doubleToInt.type , InputType, IntermediateType],
mapper2: Mapper.Aux[intToDouble.type, IntermediateType, ResultType]
): ResultType = {
input.map(doubleToInt).map(intToDouble)
}
convert(2.5 :: HNil)
as we can see the type of convert contains a type parameter IntermediateType that is completely irrelevant to the caller of this function. Is it posible to hide it somehow? It's a problem because I would like to call like this:
convert[SomeType, SomeType2, _](2.5 :: HNil)
but it doesn't compile because of unbound type parameter.
The normal solution when you want to infer only some type parameters of a method call is to split it into 2: the first fixes parameters you are interested in and returns a helper with apply method which is called to infer the rest. Applying this here, you'd get
def convert[ResultType <: HList] = new ConvertTo[ResultType]
class ConvertTo[ResultType <: HList] {
def apply[InputType <: HList, IntermediateType <: HList](input: InputType)(implicit mapper1: Mapper.Aux[doubleToInt.type, InputType, IntermediateType],
mapper2: Mapper.Aux[intToDouble.type, IntermediateType, ResultType]
) = input.map(doubleToInt).map(intToDouble)
}
Usage: convertTo[SomeType].apply(2.5 :: HNil). InputType is inferred from input and then the compiler finds the implicit mapper1 and infers IntermediateType.
Or at least, ideally it would: due to the way Scala type inference works, it can't infer IntermediateType from mapper1 and then use it in mapper2, so I am not sure it'll find mapper2 correctly. In future Scala versions this should be fixed by def apply(implicit mapper1: ...)(implicit mapper2: ...), but the pull request enabling this wasn't accepted in time for 2.12.0. If this turns out to be a problem, you'll have to split ConvertTo.apply in 2 again.
Related
Trying to figure out why the code compiles for nested type inference on method with arity-2 but not with currying.
object Test
{
trait Version
object VersionOne extends Version
trait Request[A <: Version]
trait RequestOne extends Request[VersionOne.type]
case class HelloWorld() extends RequestOne
def test1[A <: Version, T <: Request[A]](t : T, a : A): T = t
def test2[A <: Version, T <: Request[A]](t : T)(a : A): T = t
}
// This works
Test.test1(Test.HelloWorld(), Test.VersionOne)
// This doesn't
Test.test2(Test.HelloWorld())(Test.VersionOne)
test2 fails to compile with the following error:
Error:(22, 73) inferred type arguments [Nothing,A$A96.this.Test.HelloWorld] do not conform to method test2's type parameter bounds [A <: A$A96.this.Test.Version,T <: A$A96.this.Test.Request[A]]
def get$$instance$$res1 = /* ###worksheet### generated $$end$$ */ Test.test2(Test.HelloWorld())(Test.VersionOne)
Looking forward to some insights on the same.
#DmytroMitin already explained why it does fail.
However, you can solve the problem this way, using the Partially-Applied Type trick, together with Generalized Type Constraints.
def test2[T](t: T): Test2PartiallyApplied[T] = new Test2PartiallyApplied(t)
final class Test2PartiallyApplied[T](private val t: T) extends AnyVal {
def apply[A <: Version](a: A)(implicit ev: T <:< Request[A]): T = t
}
Which you can use like this.
Test.test2(Test.HelloWorld())(Test.VersionOne)
// res: HelloWorld = HelloWorld()
Nothing in compile error usually means that some type parameters were not inferred.
Try to specify them explicitly
Test.test2[Test.VersionOne.type, Test.RequestOne](Test.HelloWorld())(Test.VersionOne)
Difference between test1 and test2 is not only in currying. For example, generally in test2(t: ...)(a: ...) type of a can depend on value of t. So for test2 type inferrence is more complicated than for test1.
Scala type inference and multiple arguments list
Type inference with type aliases and multiple parameter list function
Multiple parameter closure argument type not inferred
What's the difference between multiple parameters lists and multiple parameters per list in Scala?
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
I am still trying to get my head around Shapeless (and, to a lesser extent, Scala!) and I have been writing some simple code to generate random instance data for case classes - predominantly based on the guides here: http://enear.github.io/2016/09/27/bits-of-shapeless-2/ (the example covers a JSON Writer implementation)
I have created a Generator[A] trait and created implicit implementations for simple types, and as per the example in the above link, I have also created implicit implementations to handle HList, HNil, Coproduct and CNil:
implicit def hnilGenerator = new Generator[HNil] {
override def generate(a: HNil) = HNil
}
implicit def hconsGenerator[H, T <: HList](implicit headGen: Generator[H], tailGen: Generator[T]) =
new Generator[H :: T] {
override def generate(a: H :: T) = headGen.generate(a.head) :: tailGen.generate(a.tail)
}
implicit def cnilGenerator: Generator[CNil] =
new Generator[CNil] {
override def generate(a: CNil): CNil = throw new RuntimeException("Invalid candidate configuration")
}
implicit def cconsGenerator[H, T <: Coproduct] =
new Generator[H :+: T] {
override def generate(a: H :+: T) = throw new RuntimeException("Invalid candidate configuration")
}
I can now use this code to generate a random instance based on a case class or a sealed trait:
it("should work with a case class to hlist") {
case class Test(x: IntGene, y: DoubleGene, z: BooleanGene)
val c = Generic[Test].to(Test(IntGene(), DoubleGene(), BooleanGene()))
generate(c)
}
it("what happens with sealed traits") {
sealed trait Shape
case class Square(width: Int, height: Int) extends Shape
case class Circle(radius: Int) extends Shape
val c = Generic[Shape].to(Circle(1))
generate(c)
}
Both of the above work no problem, however, if I try to make this a generic (as in parameter types) I get compilation errors not being able to find the necessary implicts:
it("should handle generics") {
case class GenericTest[A: Generic](x: A) {
def convert() = {
val c = Generic[A].to(x)
generate(c)
}
}
}
So from my understanding, because I have used the Generic context bound A, the compiler knows that is going to be available, so c must be some possible return from the call to(x) - Am I missing something in the implementation to handle that return type from the Generic shapeless call? Or have I wildly misunderstood something?
I am hoping this is possible and I have just missed something - is it that the compiler doesn't know what will be passed in (Im assuming not), or is there another possible type that needs to be handled implicitly from that to(x) call?
EDIT
Compile error added below - I'm really just trying to understand: Is it that there is some return case from the to(x) shapeless call that I have not catered for, or is it because the compiler doesn't have any idea what will be passed in and there are some types not catered for (e.g. I haven't added a Date generator implicit - and a case class could potentially have any type included? I was hoping that was not the case, and as the compiler knows nothing is actually being passed to the class/method it knows there are no issues?)
GeneratorSpec.scala:44: could not find implicit value for parameter gen: io.github.robhinds.genotype.Generator[GenericTest.this.evidence$1.Repr]
generate(c)
And my generate method is just a simple helper method that gets given the implicit Generator :
def generate[A](a: A)(implicit gen: Generator[A]) = gen.generate(a)
Your problem comes from the fact that Generic only converts a case class to a HList and a sealed trait to a Coproduct (not recursively).
So if you have a generic Generic, you have no information on what HList or Coproduct you are given, so, you cannot use your product and coproduct rules to find the wanted implicit. In some explicit case, you might have the same problem, so I will give you this as an example:
Let's say you have a case class architecture
case class Bottom(value: String)
case class Top(bot: Bottom, count: Int)
The implicit Generic[Top] will have type MyHList = Bottom :: Int :: HNil as output type, so you'll be asking for an implicit Generator[MyHList]. But since you don't have an implicit Generator[Bottom] in scope, you won't be able to use hconsgenerator.
In the generic case, it's even worse. The compiler can only infer HList for the output type of Generic[A] (and that's assuming you forget about Coproduct), so you need an implicit Generator[HList], which you cannot provide.
The solution is to give an implicit for constructs that have a generic that can itself be generated:
implicit def generic2generate[T, L <: HList](implicit generic: Generic.Aux[T, L], lGen: Generator[L]): Generator[T] = new Generator[T] {
def generate(c: T) = generic.from(lGen.generate(generic.to(c)))
}
EDIT
You can now follow the implicit resolution for our Top type:
We can have a Generator[Top] using last rule, if we have a Generic.Aux[Top, L] for some L, and a Generator[L].
The only Generic.Aux[Top, _] that exists implicitly is a Generic.Aux[Top, Bottom :: Int :: HNil], so we are reduced to finding a Generator[Top, Bottom :: Int :: HNil]
using the hcons rule three times, we are reduced to finding a Generator[Bottom], a Generator[Int] and a Generator[HNil].
Generator[Int] is given (I assume) and Generator[HNil] is the first rule, so we are reduced to finding a Generator[Bottom]
the only rule that can provide one, is once again the 3rd rule, so we must find a Generator[String :: HNil], since the only Generic available is a Generic.Aux[Bottom, String :: HNil].
using the hcons rule, we are down to finding a Generator[String], which can easily be provided.
This example shows different points:
first that it may take a long time when compiling to solve all these implicits (I only gave the main points of the proof, but the compiler has to try all possible branches)
second, that this resolution can only be done for a specific Generic, it cannot be inferred generically (although this might seem counter-intuitive); even if the human mind is able to tell that it will work for every Generic, the compiler cannot process it as such.
I think you are missing the Generator type bound.
it("should handle generics") {
case class GenericTest[A: Generic : Generator](x: A) {
def convert() = {
val c = Generic[A].to(x)
generate(c)
}
}
}
Let's assume the following:
class Wrapper1 {
case class Condition(test: String)
}
object Wrapper1 extends Wrapper1
class Wrapper2 {
case class Condition[A](test: String)
}
object Wrapper2 extends Wrapper2
class Test
type T = // whatever
def test(fn: T => Wrapper1.Condition): X
def test[R](fn: T => Wrapper2.Condition[R]): X
}
The problem is that because of type erasure those methods have the exact same type after erasure. It's easy to change the signature of the second with say:
def test[R](fn: T => Wrapper2.Condition[R])(implicit ev: R =:= R): X
But that confuses the compiler and using the test method in other places is impossible. For a number of design reasons I'm trying to keep the name of this method consistent. Is there any way to successfully do this?
Seems this is NOT a duplicate of Scala double definition (2 methods have the same type erasure)
My suggestion... Just have a single method
def test[Cond: TypeTag](fn: T => Cond): X = {
if(typeOf[T] <:< typeOf[Wrapper1.Condition]) ...
else if(typeOf[T] <:< typeOf[Wrapper2.Condition[_]) ...
else ...
}
Finally able to work it out. What I was after was a way to conditionally append to an HList. Basically multiple overloads of the same method had to co-exist because they would return a different kind of HList.
To only have one method, arguments needed to dictate what the final signature of the method looked like.
So let's assume the following, the test method had to return
def test[RR](arg: Type1[RR]): Wrapper[RR :: ExistingHList]
Or respectively:
def test(arg: Type2): Wrapper[ExistingList]
The same method would have to append to an Hlist depending on whether or not the argument satisfied certain conditions. The fix was as trivial as using shapeless.ops.hlist.Prepend, which will by default returning the existing HList in the event we try to append HNil to an hlist.
Making the argument of Type2 return an HNil or implicitly convert to something that would end up being HNil was the approach that worked.
Specifically:
class Wrapper[PS <: HList] {
def test[
RR,
HL <: HList,
Out <: HList
](
condition: T => Wrapper[HL]
)(implicit prepend: Prepend.Aux[HL, PS, Out]): Wrapper[Out[
}
This approach works if the condition function will return a Wrapper[HNil] for situations where the function doesn't need to modify the end return type. Functions who do modify are simply free to build up their own HList independently.
Still struggling with this.types (singleton types). Assume this scenario:
trait Sys[A <: Access] {
def in[T](v: String): AccessPrepare[A]
}
trait AccessPrepare[A <: Access] {
val a: A
def apply[T](fun: a.type => T): T
}
object Ref {
def single[A <: Access, V](v: V)(implicit a: A): Ref[A, V] = ???
}
trait Ref[A, V]
trait Access {
def set(r: Ref[this.type, Int]): Unit
}
The following fails:
def test(sys: Sys[Access]): Unit =
sys.in("v1") { implicit a =>
val r = Ref.single(44)
a.set(r)
}
because apparently r is of type Ref[Access, Int] and not Ref[a.type, Int]. My guess is the problem is that I would need a line like
def single[A <: Access, V](v: V)(implicit a: A): Ref[a.type, V] = ...
which isn't compiling as due to "illegal dependent method type"...
Any ideas how I can fix this. The demand is that I do not explicitly annotate calls with types. That is, I do not want to write Ref.single[a.type, Int](44)(a) for comprehensible reasons.
EDIT
As a clarification, with reference to answer "FYI, and to close the question" in thread Constraining an operation by matching a type parameter to an argument's path-dependent type -- what I would like to have in addition is the possibility to create objects (Refs) not by using a factory method in the Access but somewhere outside (e.g. with a new statement). Because the system cannot be limited by the definition of Access, I must be able to extend it with further objects.
You have several possibilities. With Scala 2.8/2.8.1, you can use the private option -Ydependent-method-types and then your solution with
def single[ A <: Access, V ]( v: V )( implicit a: A ) : Ref[ a.type, V ] = // ...
compiles fine.
If you want to avoid dependent method types because it's a private option, you can still make your first proposal compile by explicitly typing the call to Ref.single:
val r = Ref.single[a.type, Int](44)
You need to specify the type, though, as singleton types are never inferred. You problem is not the same as, but related to, the problem that singleton types are not inferred: see How to correctly type-annotate this HList?