I'm trying to create an automatic adapter from two case classes which have different fields with different types and different order.
It works, sometimes. It's just very difficult to debug and know which implicit fails to be resolved.
When the compiler fails to find an specific adapter I have to start playing in test with each one of the needed implicits and check which one is failing and why. Is there a way to mark why a given implicit method was a candidate and stopped being when trying to resolve the nested implicits?
Sorry if it sounds confusing!
private implicit def fromRecordAdapter
[
Source, SourceFields <: HList, SourceRecord <: HList, Target, TargetFields <: HList, TargetRecord <: HList,
SelectedFields <: HList, AddedFields <: HList,
SourceTakenProduct <: HList, TargetProduct <: HList, CommonRecord <: HList, AddedProduct <: HList,
AddedRecord <: HList, UnorderedRecord <: HList
](
implicit
sourceRecord: LabelledGeneric.Aux[Source, SourceRecord]
, sourceFields: Keys.Aux[SourceRecord, SourceFields]
, targetRecord: LabelledGeneric.Aux[Target, TargetRecord]
, recordFields: Keys.Aux[TargetRecord, TargetFields]
, selectedFields: hlist.Intersection.Aux[SourceFields, TargetFields, SelectedFields]
, addedFields: hlist.Diff.Aux[TargetFields, SelectedFields, AddedFields]
, sourceTakenProduct: SelectAll.Aux[SourceRecord, SelectedFields, SourceTakenProduct]
, targetProduct: SelectAll.Aux[TargetRecord, SelectedFields, TargetProduct]
, addedProduct: SelectAll.Aux[TargetRecord, AddedFields, AddedProduct]
, sourceToTargetMapper: TypeMapper[TargetProduct, SourceTakenProduct]
, addedProductDefault: Lazy[DefaultValue[AddedProduct]]
, commonRecord: ZipWithKeys.Aux[SelectedFields, TargetProduct, CommonRecord]
, addedTagger: ZipWithKeys.Aux[AddedFields, AddedProduct, AddedRecord]
, prepend: Prepend.Aux[CommonRecord, AddedRecord, UnorderedRecord]
, align: hlist.Align[UnorderedRecord, TargetRecord]
): Adapter[Source, Target] = ...
In other words, I'd love to know in which parameter from that long list, the compiler stops and says, for example: "damn, there's no implicit for sourceToTargetMapper:TypeMapper[TargetProduct, SourceTakenProduct]"
That way I'd know where to start checking
You can try
scalacOptions += "-Xlog-implicits"
Doesn't compiler write that if you try to call fromRecordAdapter explicitly?
def foo(implicit i: Int, b: Boolean, s: String, l: Long): Unit = ()
implicit val int: Int = 1
implicit val bool: Boolean = true
foo
//Error: could not find implicit value for parameter s: String
//Error: not enough arguments for method foo: (implicit i: Int, implicit b: Boolean, implicit s: String, implicit l: Long)Unit.
//Unspecified value parameter s.
Related
Can someone explain me what the difference between these two approaches for typeclass instance derivation (specifically for Option[A])?
1.
trait MyTrait[A] {...}
object MyTrait extends LowPriority {
// instances for primitives
}
trait LowPriority extends LowestPriority {
final implicit def generic[A, H <: HList](
implicit gen: Generic.Aux[A, H],
h: Lazy[MyTrait[H]]
): MyTrait[A] = ???
final implicit val hnil: MyTrait[HNil] = ???
final implicit def product[H, T <: HList](
implicit h: Lazy[MyTrait[H]],
t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
// special instances for Options
trait LowestPriority {
implicit def genericOption[A, Repr <: HList](
implicit gen: Generic.Aux[A, Repr],
hEncoder: Lazy[MyTrait[Option[Repr]]]
): MyTrait[Option[A]] = ???
implicit val hnilOption: MyTrait[Option[HNil]] = ???
implicit def productOption1[H, T <: HList](
implicit
head: Lazy[MyTrait[Option[H]]],
tail: Lazy[MyTrait[Option[T]]],
notOption: H <:!< Option[Z] forSome { type Z }
): MyTrait[Option[H :: T]] = ???
implicit def product2[H, T <: HList](
implicit
head: Lazy[MyTrait[Option[H]]],
tail: Lazy[MyTrait[Option[T]]
): MyTrait[Option[Option[H] :: T]] = ???
}
trait MyTrait[A] {...}
object MyTrait extends LowPriority {
// instances for primitives
}
trait LowPriority {
// deriving instances for options from existing non-option instances
final implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] = ??? // <<<----
final implicit def generic[A, H <: HList](
implicit gen: Generic.Aux[A, H],
h: Lazy[MyTrait[H]]
): MyTrait[A] = ???
final implicit val hnil: MyTrait[HNil] = ???
final implicit def product[H, T <: HList](
implicit h: Lazy[MyTrait[H]],
t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
I tried both and they worked correctly, but i'm not sure that they will produce the same results for all cases (maybe i've missed something).
Do we really need LowestPriority instances for this?
Am i right if i would say that the first approach gives us just a little bit more flexibility?
I assuming that by "worked correctly" you mean "compiled" or "worked for some simple use case".
Both of your examples deal with generic product types, but not with generic sum types, so there is no risk that e.g. Option[A] could get derived using Some[A] :+: None :+: CNil, which would enforce some ambiguity. So (as far as I can tell) you could write the second version like:
trait MyTrait[A] {...}
object MyTrait extends LowPriority {
// instances for primitives
// deriving instances for options from existing non-option instances
final implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] = ???
}
trait LowPriority {
// <<<----
final implicit def hcons[A, H <: HList](
implicit gen: Generic.Aux[A, H],
h: Lazy[MyTrait[H]]
): MyTrait[A] = ???
final implicit val hnil: MyTrait[HNil] = ???
final implicit def product[H, T <: HList](
implicit h: Lazy[MyTrait[H]],
t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
and it would derive things correctly.
But how 1. and 2. differs?
In second version you can derive MyTrait[Option[A]] if you can derive for A, and you can derive for any A which is primitive/option/product - so Option[Option[String]], Option[String] and Option[SomeCaseClass] should all work. It should also work if this SomeCaseClass contains fields which are Options, or other case classes which are Options, etc.
Version 1. is slightly different:
at first you are looking for primitives
then you try to derive for a product (so e.g. Option would not be handled here)
then you do something weird:
genericOption assumes that you created a Option[Repr], and then I guess map it using Repr
in order to build that Repr you take Option[HNil] and prepend types inside Option using productOption, which would break if someone used Option as a field
so you "fix" that by prepending an Option in a special case product2
I guess, you tested that only against case classes, because the first version would not work for:
Option for primitives (Option[String], Option[Int] or whatever you defined as primitive)
nested options (Option[Option[String]])
options for custom defined types which are not case classes but have manually defined instances:
class MyCustomType
object MyCustomType {
implicit val myTrait: MyTrait[MyCustomType]
}
implicitly[Option[MyCustomType]]
For that reason any solution with implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] is simpler and more bulletproof.
Depending on what you put directly into companion low-priority implicits might be or might not be needed:
if you defined coproducts then manual support for e.g. Option, List, Either could conflict with shapeless derived ones
if you manually implemented MyTrait implicit for some type in its companion object then it would have the same priority as implicits directly in MyTrait - so if it could be derived using shapeless you could have conflicts
For that reason it makes sense to put shapeless implicits in LowPriorityImplicits but primitives, and manual codecs for List, Option, Either, etc directly in companion. That is, unless you defined some e.g. Option[String] implicits directly in companion which could clash with "Option[A] with implicit for A".
Since I don't know your exact use case I cannot tell for sure, but I would probably go with the seconds approach, or most likely with the one I implemented in the snippet above.
Actually it's hard to say without right hand sides and actual implementations.
From information you provided it doesn't follow that the two type classes behave equivalently.
For example in the 1st approach you consider some special cases, so theoretically it's possible that you redefine some general behavior in special case differently.
By the way, Option[A] is a coproduct of Some[A] and None.type (List[A] is a coproduct of scala.::[A] and Nil.type) and sometimes it's easier to derive a type class for coproducts than for Option[A] (or List[A]).
If I have a method such as:
def f[T: Generic, U: Generic](t: T): U
Generic[T].to(t) returns type Generic[T]#Repr which I assume is a type alias for some type of HList.
Is it possible to select members from the HList and build another HList which I can convince the compiler is of type Generic[U]#Repr which I can then use to create an instance of U using Generic[U].from(myNewHList)?
I have tried many approaches but seem to be going around in circles.
When doing stuff like this in Shapeless, the best place to look is in the shapeless.ops typeclasses. In this case, since you know your second class is a strict subset of your first, Intersection is sufficient to get what you want. You'll want to set this up as a type class, so that you can pass in your input and output types and let the compiler infer the intermediate stuff.
trait Converter[A,B] {
def convert(a: A): B
}
object Converter {
def apply[A,B](implicit converter: Converter[A,B]) = converter
implicit def genericConverter[A, B, ARepr <: HList, BRepr <: HList](
implicit
genA: Generic.Aux[A,ARepr],
genB: Generic.Aux[B,BRepr],
intersection: shapeless.ops.hlist.Intersection.Aux[ARepr,BRepr,BRepr]
): Converter[A,B] =
new Converter[A,B]{def convert(a: A): B = genB.from(intersection(genA.to(a)))}
}
This can be used as follows:
case class Foo(a: Int, b: Int, c: String)
case class Bar(a: Int, c: String)
val foo = Foo(1,2,"Three")
val bar: Bar = Converter[Foo, Bar].convert(foo)
I'm trying to print peano numbers, something like this:
sealed trait Nat
trait _0 extends Nat
trait Succ[N <: Nat] extends Nat
type _1 = Succ[_0]
type _2 = Succ[_1]
class RepNat[T <: Nat](val value: Int)
def rep[T <: Nat](implicit r: RepNat[T]) = r.value
implicit val repZero = new RepNat[_0](0)
implicit def repSucc[A <: Succ[B], B <: Nat](implicit r: RepNat[B]): RepNat[A] = new RepNat[A](r.value + 1)
println(rep[_0])
println(rep[_1])
// does not work, implicits do not resolve recursively:
// implicitly[RepNat[_2]]
// println(rep[_2])
// but explicit instantiation works:
println(rep[_2](repSucc(implicitly[RepNat[_1]])))
Recursive implicits do work. The following definition works fine for _2:
implicit def repSucc[A <: Nat, B <: Nat](implicit
ev: A <:< Succ[B],
r: RepNat[B]
): RepNat[A] =
new RepNat[A](r.value + 1)
The reason, I believe, is that
repSucc gets a single actual type argument A, and needs to calculate B from that. With your definition it tries to assign A and B at the same time, and thus B gets assigned effectively to Nothing.
This a common problem with type inference in Scala, and the usual solution is to move the type bound A <: M[B] to a generalised type constraint A <:< M[B].
Also, note that the order of implicit parameters matters: first the compiler calculates B from A with ev: A <:< Succ[B], and then finds the RepNat implementation for B, possibly recursively.
I've got the following code to basically iterate through the fields of a case class and map them using a Poly to the same type and use ToList[HL, Out]
For simplicity we can assume the Poly does this:
object Schema extends Poly1 {
implicit def caseInt = at[Int](_ => "I'm an int")
implicit def caseString = at[String](_ => "Oh boy a string")
}
def infer[V1 <: Product, Out <: HList, MapperOut <: HList](v1: V1)(
implicit gen: Generic.Aux[V1, Out],
map: Mapper.Aux[Schema.type, Out, MapperOut],
to: ToList[MapperOut, String]
): List[String] = to (gen to v1 map Schema)
This is all very straightforward and works very well for simple scenarios:
case class Test(id: Int, text: String)
val list = infer(Test(2, "text"))
// List("I'm an int", "Oh boy a string")
Now going out to where the buses don't run:
class Automagical[T <: Product with Serializable : TypeTag] {
def instance: T
// The typetag bit is needed for something else
def convert: List[String] = infer(instance)
}
Sadly any call to above fails with:
could not find implicit value for parameter gen: shapeless.Generic.Aux[T,Out]
Bonus
How can I improve the infer method by not requiring an instance of T at all? Obviously type inference is fine, but I do need to somehow materialise a List[String] from an HList[Lub] and map over something.
Given I only ever care about the types, is it possible to derive a concrete instance of List[String] by only knowing the types to be poly mapped encoded as an HList?
Something like:
def infer[V1 <: Product, Out <: HList, MapperOut <: HList]()(
implicit gen: Generic.Aux[V1, Out],
map: Mapper.Aux[Schema.type, Out, MapperOut],
to: ToList[MapperOut, String]
): List[String] = {
// Magically build an HList with samples from its types.
// Or an alternative approach that gives the same outcome
val reifiedInstance = reify[Out]
to (reifiedInstance map Schema)
}
To ensure that convert has access to the implicit parameters for infer, they have to be present when the Automagical instance is created:
abstract class Automagical[T <: Product with Serializable : TypeTag,
Out <: HList, MapperOut <: HList]
(implicit
gen: Generic.Aux[T, Out],
map: Mapper.Aux[Schema.type, Out, MapperOut],
to: ToList[MapperOut, String]) {
def instance: T
// The typetag bit is needed for something else
def convert: List[String] = infer(instance)
}
I want to find out if a particular class member is present in a given case class or not.
Following does give me that answer, failing at compile time which is right. (credit to Travis Brown)
scala> def getIntId[A, R <: HList](a: A)(implicit
| gen: LabelledGeneric.Aux[A, R],
| sel: Selector.Aux[R, Witness.`'id`.T, Int]
| ): Int = sel(gen.to(a))
case class Foo(id: String)
case class Bar(id: Int, name: String)
scala> getIntId(Bar(123, "bar"))
res3: Int = 123
scala> getIntId(Foo("12345"))
<console>:15: error: could not find implicit value for parameter sel: shapeless.ops.record.Selector.Aux[R,Symbol with shapeless.tag.Tagged[String("id")],Int]
getIntId(Foo("12345"))
Now if I don't have a concrete type but T to pass into getIntId method, is there a way to get this to work with generic type T ?
Update
Say have a method called findIdFromType as described below which takes in type T (where T will always be a some case class). is it possible to make this work?
def findIdFromType[T](t:T) = {
getIntId(t) //as expected doesn't compile
}
getIntId requires two implicit parameters.
If you need to call it in the context of a local method, you need to prove that those parameters exist in such context.
In order to do so, you have to propagate the implicit evidence, like this
def findIdFromType[A, R <: HList](a: A)(implicit
gen: LabelledGeneric.Aux[A, R],
sel: Selector.Aux[R, Witness.`'id`.T, Int]): Int = getIntId(a)
This of course is a completely useless example, but in case you want to perform other operations inside the wrapping method, this is the way to go: propagate the implicit evidence.