Scala Shapeless No Implicits Found Case Class Migration Adding New Field - scala

In order to get familiar with shapeless, I did investigate into the examples of the shapeless-guide. I am especially interested in case class migrations. Therefore, I did dive into the case study of the case class migration section of the shapeless-guide.
Unfortunately, the code of the case class migration case study is not compiling as the last conversion where a field is added is failing as no implicits can be found.
I am trying to understand the issue with the code. However, understanding the issue is quite difficult and I failed so far. Hence, i would like to ask whether somebody can give an hint.
I tried also several compilers, i.e. scala 2.12, scala 2.13.
I did run the compiler with the -Xlog-implicits flag which gives several error messages:
[info] Icecream.scala:67:42: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[Icecream.IceCreamV1,V] because:
[info] type parameters weren't correctly instantiated outside of the implicit tree: inferred type arguments [String :: Int :: Boolean :: shapeless.HNil,Nothing] do not conform to method materializeCoproduct's type parameter bounds [V <: shapeless.Coproduct,R <: shapeless.Coproduct]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: shapeless.this.LabelledGeneric.materializeCoproduct is not a valid implicit value for shapeless.LabelledGeneric.Aux[Icecream.IceCreamV1,ARepr] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[Icecream.IceCreamV1,V]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[Icecream.IceCreamV2c,V] because:
[info] type parameters weren't correctly instantiated outside of the implicit tree: inferred type arguments [String :: Boolean :: Int :: Int :: shapeless.HNil,Nothing] do not conform to method materializeCoproduct's type parameter bounds [V <: shapeless.Coproduct,R <: shapeless.Coproduct]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: shapeless.this.LabelledGeneric.materializeCoproduct is not a valid implicit value for shapeless.LabelledGeneric.Aux[Icecream.IceCreamV2c,BRepr] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[Icecream.IceCreamV2c,V]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: hlist.this.Prepend.hnilPrepend1 is not a valid implicit value for shapeless.ops.hlist.Prepend.Aux[shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out,shapeless.labelled.FieldType[Symbol ## String("name"),String] :: shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int] :: shapeless.labelled.FieldType[Symbol ## String("inCone"),Boolean] :: shapeless.HNil,Unaligned] because:
[info] typing TypeApply reported errors for the implicit tree: type arguments [shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out,shapeless.labelled.FieldType[Symbol ## String("name"),String] :: shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int] :: shapeless.labelled.FieldType[Symbol ## String("inCone"),Boolean] :: shapeless.HNil] do not conform to method hnilPrepend1's type parameter bounds [P <: shapeless.HNil,S <: shapeless.HList]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: hlist.this.Prepend.hnilPrepend0 is not a valid implicit value for shapeless.ops.hlist.Prepend.Aux[shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out,shapeless.labelled.FieldType[Symbol ## String("name"),String] :: shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int] :: shapeless.labelled.FieldType[Symbol ## String("inCone"),Boolean] :: shapeless.HNil,Unaligned] because:
[info] typing TypeApply reported errors for the implicit tree: type arguments [shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out,shapeless.labelled.FieldType[Symbol ## String("name"),String] :: shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int] :: shapeless.labelled.FieldType[Symbol ## String("inCone"),Boolean] :: shapeless.HNil] do not conform to method hnilPrepend0's type parameter bounds [P <: shapeless.HList,S <: shapeless.HNil]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: hlist.this.Remove.recurse is not a valid implicit value for shapeless.ops.hlist.Remove.Aux[shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int] :: shapeless.HNil,shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int],(shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int], OutT)] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter r: shapeless.ops.hlist.Remove.Aux[shapeless.HNil,shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int],(shapeless.labelled.FieldType[Symbol ## String("numCherries"),Int], OutT)]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
[info] ^
[info] Icecream.scala:67:42: hlist.this.Remove.recurse is not a valid implicit value for shapeless.ops.hlist.Remove.Aux[shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int] :: shapeless.HNil,shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int],(shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int], R)] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter r: shapeless.ops.hlist.Remove.Aux[shapeless.HNil,shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int],(shapeless.labelled.FieldType[Symbol ## String("numWaffles"),Int], OutT)]
[info] IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c]
The full code is as following:
case class IceCreamV1(name: String, numCherries: Int, inCone: Boolean)
case class IceCreamV2a(name: String, inCone: Boolean)
case class IceCreamV2b(name: String, inCone: Boolean, numCherries: Int)
case class IceCreamV2c(name: String, inCone: Boolean, numCherries: Int, numWaffles: Int)
trait Migration[A, B] {
def apply(a: A): B
}
implicit class MigrationOps[A](a: A) {
def migrateTo[B](implicit migration: Migration[A, B]): B =
migration.apply(a)
}
def createMonoid[A](zero: A)(add: (A, A) => A): Monoid[A] =
new Monoid[A] {
def empty = zero
def combine(x: A, y: A): A = add(x, y)
}
implicit val hnilMonoid: Monoid[HNil] = createMonoid[HNil](HNil)((x, y) => HNil)
implicit def emptyHList[K <: Symbol, H, T <: HList](
implicit
hMonoid: Lazy[Monoid[H]],
tMonoid: Monoid[T]
): Monoid[FieldType[K, H] :: T] =
createMonoid(field[K](hMonoid.value.empty) :: tMonoid.empty) {
(x, y) =>
field[K](hMonoid.value.combine(x.head, y.head)) ::
tMonoid.combine(x.tail, y.tail)
}
implicit def genericMigration[
A, B, ARepr <: HList, BRepr <: HList,
Common <: HList, Added <: HList, Unaligned <: HList
](
implicit
aGen: LabelledGeneric.Aux[A, ARepr],
bGen: LabelledGeneric.Aux[B, BRepr],
inter: hlist.Intersection.Aux[ARepr, BRepr, Common],
diff: hlist.Diff.Aux[BRepr, Common, Added],
monoid: Monoid[Added],
prepend: hlist.Prepend.Aux[Added, Common, Unaligned],
align: hlist.Align[Unaligned, BRepr]
): Migration[A, B] =
new Migration[A, B] {
def apply(a: A): B = bGen.from(align(prepend(monoid.empty, inter(aGen.to(a)))))
}
IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2a]
// res14: IceCreamV2a = IceCreamV2a(Sundae,true)
IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2b]
// res15: IceCreamV2b = IceCreamV2b(Sundae,true,1)
IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2c] // <- fails as implicit can not be found
// res16: IceCreamV2c = IceCreamV2c(Sundae,true,1,0)

Make sure that you have an instance of type class Monoid for type Int (i.e. implicit Monoid[Int]). For example check that implicitly[Monoid[Int]] compiles. Migration from IceCreamV1 into IceCreamV2c is adding new Int field so you need empty value for Int (i.e. 0).
So either use cats.Monoid (in fresh Cats you no longer need to add extra imports for instances of type classes since they are defined in companion objects) or if you define your custom Monoid
trait Monoid[A] {
def empty: A
def combine(x: A, y: A): A
}
then add
implicit val intMonoid: Monoid[Int] = createMonoid[Int](0)(_ + _)

Related

Family polymorphism with ScalaTest

I'm trying to achieve something with ScalaTest and based on C. Hosrtmann's "Scala for the impatient" I think this is related to "Family polymorphism". Apparently I don't understand something about Scala's type system, so I fail.
To cut the long story short, this is what I'm trying to do.
I have a base (production) trait:
trait MaxPQ[Key] {
implicit protected val cmp: Ordering[_ >: Key]
...
def insert(v: Key): Unit
def max(): Key
def delMax(): Key
...
}
then there is a number of implementations (that use either a backing tree or an array).
In tests I want to create an abstract structure which would allow to test any implementation for three Ordered Keys: Char, Int, Double.
First I've written two behaviors (for empty and non-empty priority queues). Here's a snippet:
trait MaxPQBehaviours {
// underlying tests spec
self: BaseSpec =>
def nonEmptyMaxPQ[T <: Ordered[T], ImplLà <: MaxPQ[T]](instanceSupplier: () => ImplLà, sortedInput: List[T]): Unit = {
...
behavior of "size"
it should s"be equal to ${sortedInput.size}" in {
val instance = instanceSupplier()
instance.size() shouldEqual sortedInput.size
}
behavior of "max"
it should s"return the expected $max" in {
val instance = instanceSupplier()
instance.max() shouldEqual max
}
...
Finally to add a last layer of abstraction I'm adding a BaseMaxPQSpec that mixes-in the above MaxPQBehaviours and calls its behaviors for three abstract MaxPQ types. Here I'only provide example for Char:
trait BaseMaxPQSpec extends BaseSpec with MaxPQBehaviours {
type CharMaXPQ <: MaxPQ[Char]
def charMaxPQ: CharMaXPQ
val sortedCharsList: List[Char] = List[Char]('C', 'b', 'd', 'a', 'z').sorted
it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
}
And this is where compiler spits this at me:
[error] ~/Algorithms/Chapter 2 Sorting/algorithms2_1/src/test/scala/ca/vgorcinschi/algorithms2_4/BaseMaxPQSpec.scala:18:25: inferred type arguments [Char,BaseMaxPQSpec.this.CharMaXPQ] do not conform to method nonEmptyMaxPQ's type parameter bounds [T <: Ordered[T],ImplLà <: ca.vgorcinschi.algorithms2_4.MaxPQ[T]]
[error] it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
[error] ^
[error] ~/Algorithms/Chapter 2 Sorting/algorithms2_1/src/test/scala/ca/vgorcinschi/algorithms2_4/BaseMaxPQSpec.scala:18:42: type mismatch;
[error] found : () => BaseMaxPQSpec.this.CharMaXPQ
[error] required: () => ImplLà
[error] it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
[error] ^
[error] ~/Algorithms/Chapter 2 Sorting/algorithms2_1/src/test/scala/ca/vgorcinschi/algorithms2_4/BaseMaxPQSpec.scala:18:56: type mismatch;
[error] found : List[Char]
[error] required: List[T]
[error] it should behave like nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
[error] ^
[error] three errors found
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 12 s, completed Feb 15, 2020 6:21:35 PM
What is the correct way to set-up my testing framework? Please don't hesitate to ask for details, clarifications.
Given
nonEmptyMaxPQ(() => charMaxPQ, sortedCharsList)
type inference deduces type T = Char because the type of charMaxPQ is MaxPQ[Char], thus
T <: Ordered[T]
becomes Char <: Ordered[Char]
which certainly is not true. Perhaps try specifying Ordering like so
def nonEmptyMaxPQ[T: Ordering, Impl <: MaxPQ[T]](instanceSupplier: () => Impl, sortedInput: List[T])
Note the difference between upper bound T <: Ordered[T] and context bound T: Ordering.

Defining instances of a third-party typeclass, implicit not found but explicit works fine

I'm working with Slick's GetResult typeclass and wanted to use Shapeless to derive instances of GetResult[Option[(A, B, C...)]]
What I want:
Given an implicit GetResult[Option[A]], GetResult[Option[B]], ...,
implicitly generate a GetResult[Option[(A, B, ...)]]
What I tried
trait CanGetOption[T] {
def getOption: GetResult[Option[T]]
}
object CanGetOption {
// convenience implicit resolver
def apply[T](implicit canGetOption: CanGetOption[T]): CanGetOption[T] = canGetOption
// base case: HNil
implicit val getHNilOption: CanGetOption[HNil] = from(GetResult { _ => Some(HNil) })
// recursion case: H :: Tail
implicit def getHConsOption[H, Tail <: HList](
implicit getHeadOption: GetResult[Option[H]],
canGetTailOption: CanGetOption[Tail]
): CanGetOption[H :: Tail] = from(GetResult[Option[H :: Tail]] { r =>
val headOpt = getHeadOption(r)
val tailOpt = canGetTailOption.getOption(r)
for(head <- headOpt; tail <- tailOpt) yield head :: tail
})
// generic case: A, given a A <-> Repr conversion
// I also tried moving this into a "LowPriorityImplicits" thing, just in case
implicit def getGenericOption[A, Repr <: HList](
implicit gen: Generic.Aux[A, Repr],
getReprOpt: CanGetOption[Repr]
): CanGetOption[A] = from(GetResult { r =>
val reprOpt = getReprOpt.getOption(r)
reprOpt.map(gen.from)
})
}
implicit def resolveOptionGetter[T: CanGetOption]: GetResult[Option[T]] =
CanGetOption[T].getOption
Problem:
When I've imported the above, the resolveOptionGetter doesn't seem to be considered when searching for implicits:
scala> implicitly[GetResult[Option[(Int, Int)]]]
<console>:19: error: could not find implicit value for parameter e: scala.slick.jdbc.GetResult[Option[(Int, Int)]]
implicitly[GetResult[Option[(Int, Int)]]]
^
scala> resolveOptionGetter[(Int, Int)]
res1: scala.slick.jdbc.GetResult[Option[(Int, Int)]] = <function1>
Why can't the compiler find resolveOptionGetter in the implicit search? What can I do to help it?
The thing is that slick.jdbc.GetResult is covariant. If it were invariant, types would be inferred correctly and implicits would be resolved.
A workaround is hiding covariant slick.jdbc.GetResult with custom invariant type alias GetResult. Remove import slick.jdbc.GetResult and write in your source file
type GetResult[T] = slick.jdbc.GetResult[T]
object GetResult {
def apply[T](implicit f: PositionedResult => T): GetResult[T] = slick.jdbc.GetResult.apply
}
Now implicitly[GetResult[Option[(Int, Int)]]] compiles. Tested in Scala 2.12.7 + Shapeless 2.3.3 + Slick 3.2.3.
Variance often makes troubles for implicit resolution:
https://github.com/scala/bug/issues/10099
https://github.com/locationtech/geotrellis/issues/1292
Implicit resolution with covariance

Why could it not resolve implicits, if a Coproduct member is generic in my ADT?

I've written a self-contained example to reproduce the problem.
For better understanding of this problem I wrote a simple type class Encoder which takes care of encoding the given type T:
trait Encoder[T] {
def encode(value: T): String
}
with its companion object for nice syntax and short-hand function for creating the type class instances:
object Encoder {
def instance[T](f: T => String): Encoder[T] = new Encoder[T] {
def encode(value: T): String = f(value)
}
def apply[T](implicit encoder: Encoder[T]): Encoder[T] = encoder
object syntax {
implicit class EncoderOps[T](value: T) {
def encode(implicit encoder: Encoder[T]): String = encoder.encode(value)
}
}
}
There is an ADT called Fragment for representing the fragments of a page and I've implemented the Encoder for all fragment type.
sealed trait Fragment
final case class TextFragment(value: String) extends Fragment
object TextFragment {
implicit val encoder: Encoder[TextFragment] = Encoder.instance(_.value)
}
final case class NumberFragment(value: Int) extends Fragment
object NumberFragment {
implicit val encoder: Encoder[NumberFragment] =
Encoder.instance(_.value.toString)
}
In order to make the call like here possible:
val fragments: List[Fragment] =
List(TextFragment("text fragment"), NumberFragment(123))
println {
fragments.map(_.encode).mkString("\n")
}
I've implemented a generic Encoder's of Coproduct and HList with help of Shapeless:
implicit def hlistEncoder[H, T <: HList](
implicit
hEncoder: Lazy[Encoder[H]],
tEncoder: Encoder[T]
): Encoder[H :: T] = instance {
case h :: t => hEncoder.value.encode(h) ++ tEncoder.encode(t)
}
implicit def genericEncoder[A, Repr](implicit gen: Generic.Aux[A, Repr],
encoder: Encoder[Repr]): Encoder[A] =
instance(fragment => encoder.encode(gen.to(fragment)))
implicit val cnilEncoder: Encoder[CNil] = instance(
cnil => throw new Exception("not allowed"))
implicit def coproductEncoder[H, T <: Coproduct](
implicit hEncoder: Lazy[Encoder[H]],
tEncoder: Encoder[T]
): Encoder[H :+: T] = instance {
case Inl(h) => hEncoder.value.encode(h)
case Inr(t) => tEncoder.encode(t)
}
It works as expected, but ...
... it doesn't work if I add a generic fragment like here:
final case class GenericFragment[T](value: T) extends Fragment
object GenericFragment {
implicit def encoder[T](
implicit encoder: Encoder[T]): Encoder[GenericFragment[T]] =
Encoder.instance(fragment => encoder.encode(fragment.value))
}
and with some Encoders for Int and String values:
implicit val stringEncoder: Encoder[String] = instance(identity)
implicit val intEncoder: Encoder[Int] = instance(_.toString)
Then it doesn't compile anymore ...
val fragments: List[Fragment] =
List(TextFragment("text fragment"), NumberFragment(123))
println {
fragments.map(_.encode).mkString("\n")
}
Here an output with -Xlog-implicits in scalacOptions:
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[playground.Fragment,Repr] because:
[info] hasMatchingSymbol reported error: exception during macro expansion:
[info] java.lang.IndexOutOfBoundsException: -1
[info] at scala.collection.LinearSeqOptimized.apply(LinearSeqOptimized.scala:63)
[info] at scala.collection.LinearSeqOptimized.apply$(LinearSeqOptimized.scala:61)
[info] at scala.collection.immutable.List.apply(List.scala:86)
[info] at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$4(generic.scala:421)
[info] at scala.collection.immutable.List.map(List.scala:283)
[info] at shapeless.CaseClassMacros.substituteArgs$1(generic.scala:419)
[info] at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$3(generic.scala:446)
[info] at scala.collection.immutable.List.flatMap(List.scala:335)
[info] at shapeless.CaseClassMacros.ctorsOfAux(generic.scala:413)
[info] at shapeless.CaseClassMacros.ctorsOfAux$(generic.scala:382)
[info] at shapeless.GenericMacros.ctorsOfAux(generic.scala:989)
[info] at shapeless.CaseClassMacros.distinctCtorsOfAux(generic.scala:379)
[info] at shapeless.CaseClassMacros.distinctCtorsOfAux$(generic.scala:374)
[info] at shapeless.GenericMacros.distinctCtorsOfAux(generic.scala:989)
[info] at shapeless.CaseClassMacros.ctorsOf(generic.scala:371)
[info] at shapeless.CaseClassMacros.ctorsOf$(generic.scala:371)
[info] at shapeless.GenericMacros.ctorsOf(generic.scala:989)
[info] at shapeless.GenericMacros.mkCoproductGeneric(generic.scala:1036)
[info] at shapeless.GenericMacros.materialize(generic.scala:1003)
[info] fragments.map(_.encode).mkString("\n")
[info] ^
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: playground.this.Encoder.genericEncoder is not a valid implicit value for playground.Encoder[playground.Fragment] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[playground.Fragment,Repr]
[info] fragments.map(_.encode).mkString("\n")
[info] ^
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[playground.Fragment,Repr] because:
[info] hasMatchingSymbol reported error: exception during macro expansion:
[info] java.lang.IndexOutOfBoundsException: -1
[info] at scala.collection.LinearSeqOptimized.apply(LinearSeqOptimized.scala:63)
[info] at scala.collection.LinearSeqOptimized.apply$(LinearSeqOptimized.scala:61)
[info] at scala.collection.immutable.List.apply(List.scala:86)
[info] at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$4(generic.scala:421)
[info] at scala.collection.immutable.List.map(List.scala:283)
[info] at shapeless.CaseClassMacros.substituteArgs$1(generic.scala:419)
[info] at shapeless.CaseClassMacros.$anonfun$ctorsOfAux$3(generic.scala:446)
[info] at scala.collection.immutable.List.flatMap(List.scala:335)
[info] at shapeless.CaseClassMacros.ctorsOfAux(generic.scala:413)
[info] at shapeless.CaseClassMacros.ctorsOfAux$(generic.scala:382)
[info] at shapeless.GenericMacros.ctorsOfAux(generic.scala:989)
[info] at shapeless.CaseClassMacros.distinctCtorsOfAux(generic.scala:379)
[info] at shapeless.CaseClassMacros.distinctCtorsOfAux$(generic.scala:374)
[info] at shapeless.GenericMacros.distinctCtorsOfAux(generic.scala:989)
[info] at shapeless.CaseClassMacros.ctorsOf(generic.scala:371)
[info] at shapeless.CaseClassMacros.ctorsOf$(generic.scala:371)
[info] at shapeless.GenericMacros.ctorsOf(generic.scala:989)
[info] at shapeless.GenericMacros.mkCoproductGeneric(generic.scala:1036)
[info] at shapeless.GenericMacros.materialize(generic.scala:1003)
[info] fragments.map(_.encode).mkString("\n")
[info] ^
[info] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: playground.this.Encoder.genericEncoder is not a valid implicit value for playground.Encoder[playground.Fragment] because:
[info] hasMatchingSymbol reported error: could not find implicit value for parameter gen: shapeless.Generic.Aux[playground.Fragment,Repr]
[info] fragments.map(_.encode).mkString("\n")
[info] ^
[error] /Users/../example/src/main/scala/playground/PlaygroundApp.scala:92:21: could not find implicit value for parameter encoder: playground.Encoder[playground.Fragment]
[error] fragments.map(_.encode).mkString("\n")
Used versions:
Scala: 2.12.5
shapeless: 2.3.3
Why doesn't it work with a generic fragment type? Can somebody explain me why? Hopefully there is a good workaround to fix the problem.
Thank you for your help.
It doesn't look like the problem is introducing the generic. If we look at this code
val fragments: List[Fragment] =
List(TextFragment("text fragment"), NumberFragment(123))
we recognize that List takes as type parameter the first common bound of its types, which is Fragment. With this assignment, you have lost type information about the fragments. List is a Coproduct, but you do not have any Encoder[Fragment] in scope so the implicit def coproductEncoder is not applicable.
You either need to use an HList of EncoderOps[TextFragment] :: EncoderOps[NumberFragment]] if you really need to keep the type parameters or simply get rid of the type parameter like so:
trait Encodable{
def encode:String
}
object Encodable{
implicit def asEncodable[T](t:T)(implicit encoder:Encoder[T]) = new Encodable {
def encode = encoder.encode(t)
}
}
val encodableFragments:List[Encodable] = List(TextFragment("text fragment"), NumberFragment(123))

How to create Hlist from single element by implicit function

I am trying to create a hlist implicitly.
case class A(value: Int)
implicit def lift(single: A): A :: HNil = single :: HNil
def something[L <: HList](l: L)(implicit lUBConstraint: LUBConstraint[L, A],
isHCons: IsHCons[L]) = {
println("works")
}
something(A(1) :: A(2) :: HNil) //works
something(A(1)) //not works
something(lift(A(1))) //works
something(A(1)) is not working. However, I use intellij idea and It can detect that lift is proper to use here.
Here is the error message from compiler.
inferred type arguments [Boot.A] do not conform to method something's type parameter bounds [L <: shapeless.HList]
[error] something(A(1)) //not works [error] ^ [error]
type mismatch; [error] found : Boot.A [error] required: L [error] something(A(1))
could not find implicit value for parameter lUBConstraint: shapeless.LUBConstraint[L, Boot.A] [error] something(A(1)
IIRC Scala implicit conversions don't trigger to satisfy type bounds.
You can modify your definition of something to allow any such conversions to work:
def something[X, L <: HList](x: X)(
implicit asHList: X => L,
lUBConstraint: LUBConstraint[L, A],
isHCons: IsHCons[L]
) = {
println("works")
}
For single argument, this will use your lift function, and <:< from Predef for actual HLists

Can't prove that singleton types are singleton types while generating type class instance

Suppose I've got a type class that proves that all the types in a Shapeless coproduct are singleton types:
import shapeless._
trait AllSingletons[A, C <: Coproduct] {
def values: List[A]
}
object AllSingletons {
implicit def cnilSingletons[A]: AllSingletons[A, CNil] =
new AllSingletons[A, CNil] {
def values = Nil
}
implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit
tsc: AllSingletons[A, T],
witness: Witness.Aux[H]
): AllSingletons[A, H :+: T] =
new AllSingletons[A, H :+: T] {
def values = witness.value :: tsc.values
}
}
We can show that it works with a simple ADT:
sealed trait Foo
case object Bar extends Foo
case object Baz extends Foo
And then:
scala> implicitly[AllSingletons[Foo, Bar.type :+: Baz.type :+: CNil]].values
res0: List[Foo] = List(Bar, Baz)
Now we want to combine this with Shapeless's Generic mechanism that'll give us a coproduct representation of our ADT:
trait EnumerableAdt[A] {
def values: Set[A]
}
object EnumerableAdt {
implicit def fromAllSingletons[A, C <: Coproduct](implicit
gen: Generic.Aux[A, C],
singletons: AllSingletons[A, C]
): EnumerableAdt[A] =
new EnumerableAdt[A] {
def values = singletons.values.toSet
}
}
I'd expect implicitly[EnumerableAdt[Foo]] to work, but it doesn't. We can use -Xlog-implicits to get some information about why:
<console>:17: shapeless.this.Witness.apply is not a valid implicit value for
shapeless.Witness.Aux[Baz.type] because:
Type argument Baz.type is not a singleton type
implicitly[EnumerableAdt[Foo]]
^
<console>:17: this.AllSingletons.coproductSingletons is not a valid implicit
value for AllSingletons[Foo,shapeless.:+:[Baz.type,shapeless.CNil]] because:
hasMatchingSymbol reported error: could not find implicit value for parameter
witness: shapeless.Witness.Aux[Baz.type]
implicitly[EnumerableAdt[Foo]]
^
<console>:17: this.AllSingletons.coproductSingletons is not a valid implicit
value for AllSingletons[Foo,this.Repr] because:
hasMatchingSymbol reported error: could not find implicit value for parameter
tsc: AllSingletons[Foo,shapeless.:+:[Baz.type,shapeless.CNil]]
implicitly[EnumerableAdt[Foo]]
^
<console>:17: this.EnumerableAdt.fromAllSingletons is not a valid implicit
value for EnumerableAdt[Foo] because:
hasMatchingSymbol reported error: could not find implicit value for parameter
singletons: AllSingletons[Foo,C]
implicitly[EnumerableAdt[Foo]]
^
<console>:17: error: could not find implicit value for parameter e:
EnumerableAdt[Foo]
implicitly[EnumerableAdt[Foo]]
^
Baz.type obviously is a singleton type, though. We can try putting the Witness instances in scope manually just for fun:
implicit val barSingleton = Witness[Bar.type]
implicit val bazSingleton = Witness[Baz.type]
And somehow now it works:
scala> implicitly[EnumerableAdt[Foo]].values
res1: Set[Foo] = Set(Bar, Baz)
I don't understand why these instances would work in this context while the ones generated by the Witness.apply macro method (which we used to create them) don't. What's going on here? Is there a convenient workaround that doesn't require us to enumerate the constructors manually?
This works as written as of the most recent shapeless 2.1.0-SNAPSHOT.