Suppose I have container-marker
case class TypedString[T](value: String)
and partial function trick
abstract class PartFunc[Actual <: HList] {
val poly: Poly
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[poly.type, L1]): L1 = l
}
Poly for Mapper
object f extends (TypedString ~>> String) {
def apply[T](s : TypedString[T]) = s.value
}
and result method
def func[Actual <: HList] = new PartFunc[Actual] {
val poly = f
}
Example of usage:
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
This code fails at compile time because compiler cannot find Mapped implicit parameter:
could not find implicit value for parameter mapped:
shapeless.ops.hlist.Mapped[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],nottogether.MapperTest.TypedString]{type Out = shapeless.::[nottogether.MapperTest.TypedString[Int],shapeless.::[nottogether.MapperTest.TypedString[String],shapeless.HNil]]}
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
But if we remove Mapper implicit parameter from PartFunc.apply(...) signature all works fine. So I have no idea why and how Mapper influence on Mapped.
The compiler complains about Mapped while the actual issue is with Mapper. I am not sure why, but there seems to be going something wrong with getting a Mapped for the singleton type poly.type when poly is an abstract value or a constructor argument of PartFunc.
A solution would be to make poly a P <: Poly and passing the singleton type along with Actual when we create the PartFunc :
import shapeless._
import ops.hlist.{Mapped, Mapper}
import poly.~>>
abstract class PartFunc[Actual <: HList, P <: Poly] {
val poly: P
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type] { val poly = f }
Or with a regular class :
class PartFunc[Actual <: HList, P <: Poly](poly: P) {
def apply[L1 <: HList](l: L1)(implicit
mapped: Mapped.Aux[Actual, TypedString, L1],
mapper: Mapper[P, L1]
): mapper.Out = mapper(l)
}
def func[Actual <: HList] = new PartFunc[Actual, f.type](f)
Notice that we now have to write mapper(l), because l map poly will still look for a Mapped[poly.type, L1].
We can now call func :
func[
Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
// String :: String :: HNil = 42 :: hello :: HNil
I am sure that someone with some more in depth knowledge of the Scala type system could provide us with a clearer explanation and possibly a better solution for this problem.
Related
I'm using Shapeless to accumulate materialized values in Akka as an HList and convert that to a case class.
(You don't have to know Akka much for this question, but the default approach accumulates materialized values as recursively nested 2-tuples, which isn't much fun, so Shapeless HLists seemed a more sensible approach -- and works pretty well. But I don't know how to properly re-use that approach. Here, I'll simplify the kinds of values Akka produces.)
For example, let's say we've got two materialized types, "A" and "B":
case class Result(b: B, a: A)
createA
.mapMaterialized((a: A) => a :: HNil)
.viaMat(flowCreatingB)((list1, b: B) => b :: list1)
.mapMaterialized(list2 => Generic[Result].from(list2))
// list1 = A :: HNil
// list2 = B :: A :: HNil
... and that produces Result just fine. But it requires that your case class be written backwards -- first value last, etc -- which is kind of dorky and hard to follow.
So the sensible thing is to reverse the list before converting to the case class, like this:
case class Result(a: A, b: B)
// ...
.mapMaterialized(list2 => Generic[Result].from(list2.reverse))
Now we can think about Result properties in the same order they were built. Yay.
But how to simplify and reuse this line of code?
The problem is that implicits don't work on multiple type parameters. For example:
def toCaseClass[A, R <: HList](implicit g: Generic.Aux[A, R], r: Reverse.Aux[L, R]): R => A =
l => g.from(l.reverse)
I'd need to specify both A (Result, above) and the HList being built:
.mapMaterialized(toCaseClass[Result, B :: A :: HNil])
Obviously, that invocation is going to be absurd with long lists (and Akka tends to build up really ugly-looking materialized types, not merely "A" and "B"). It'd be nicer to write something like:
.mapMaterialized(toCaseClass[Result])
I've tried to solve this using implicits, like this:
implicit class GraphOps[Mat <: HList](g: RunnableGraph[Mat]) {
implicit def createConverter[A, RL <: HList](implicit
r: Reverse.Aux[Mat, RL],
gen: Generic.Aux[A, RL]): Lazy[Mat => A] =
Lazy { l =>
val x: RL = l.reverse
val y: A = gen.from(x)
gen.from(l.reverse)
}
def toCaseClass[A](implicit convert: Lazy[Mat => A]): RunnableGraph[A] = {
g.mapMaterializedValue(convert.value)
}
But the compiler complains "No implicit view available".
The deeper problem is that I don't quite understand how to properly infer...
// R = Reversed order (e.g. B :: A :: NHNil)
// T = Type to create (e.g. Result(a, b))
// H = HList of T (e.g. A :: B :: HNil)
gen: Generic.Aux[T, H] // Generic[T] { type Repr = H }
rev: Reverse.Aux[R, H] // Reverse[R] { type Out = H }
This is sort of backwards from how Shapeless likes to infer things; I can't quite chain the abstract type members properly.
Profound thanks if you have insight here.
My bad: the example above, of course, requires Akka to compile. A simpler way of putting it is this (with thanks to Dymtro):
import shapeless._
import shapeless.ops.hlist.Reverse
case class Result(one: String, two: Int)
val results = 2 :: "one" :: HNil
println(Generic[Result].from(results.reverse))
// this works: prints "Result(one,2)"
case class Converter[A, B](value: A => B)
implicit class Ops[L <: HList](list: L) {
implicit def createConverter[A, RL <: HList](implicit
r: Reverse.Aux[L, RL],
gen: Generic.Aux[A, RL]): Converter[L, A] =
Converter(l => gen.from(l.reverse))
def toClass[A](implicit converter: Converter[L, A]): A =
converter.value(list)
}
println(results.toClass[Result])
// error: could not find implicit value for parameter converter:
// Converter[Int :: String :: shapeless.HNil,Result]
Dymtro's final example, below...
implicit class GraphOps[Mat <: HList, R <: HList](g: RunnableGraph[Mat]) {
def toCaseClass[A](implicit
r: Reverse.Aux[Mat, R],
gen: Generic.Aux[A, R]
): RunnableGraph[A] = g.mapMaterializedValue(l => gen.from(l.reverse))
}
... does seem to do what I'd been hoping for. Thank you very much Dmytro!
(Note: I had been somewhat misled in analyzing it earlier: it seems IntelliJ's presentation compiler incorrectly insists it won't compile (missing implicits). Moral: Don't trust IJ's presentation compiler.)
If I understood correctly you wish that in
def toCaseClass[A, R <: HList, L <: HList](implicit
g: Generic.Aux[A, R],
r: Reverse.Aux[L, R]
): L => A = l => g.from(l.reverse)
you could specify only A and then R, L be inferred.
You can do this with PartiallyApplied pattern
import shapeless.ops.hlist.Reverse
import shapeless.{Generic, HList, HNil}
def toCaseClass[A] = new {
def apply[R <: HList, L <: HList]()(implicit
g: Generic.Aux[A, R],
r0: Reverse.Aux[R, L],
r: Reverse.Aux[L, R]
): L => A = l => g.from(l.reverse)
}
class A
class B
val a = new A
val b = new B
case class Result(a: A, b: B)
toCaseClass[Result]().apply(b :: a :: HNil)
(without implicit r0 type parameter L can't be inferred upon call of .apply() because L becomes known only upon call .apply().apply(...))
or better
def toCaseClass[A] = new {
def apply[R <: HList, L <: HList](l: L)(implicit
g: Generic.Aux[A, R],
r: Reverse.Aux[L, R]
): A = g.from(l.reverse)
}
toCaseClass[Result](b :: a :: HNil)
(here we don't need r0 because L becomes known already upon call .apply(...)).
If you want you can replace anonymous class with named one
def toCaseClass[A] = new PartiallyApplied[A]
class PartiallyApplied[A] {
def apply...
}
Alternatively you can define a type class (although this is a little more wordy)
trait ToCaseClass[A] {
type L
def toCaseClass(l: L): A
}
object ToCaseClass {
type Aux[A, L0] = ToCaseClass[A] { type L = L0 }
def instance[A, L0](f: L0 => A): Aux[A, L0] = new ToCaseClass[A] {
type L = L0
override def toCaseClass(l: L0): A = f(l)
}
implicit def mkToCaseClass[A, R <: HList, L <: HList](implicit
g: Generic.Aux[A, R],
r0: Reverse.Aux[R, L],
r: Reverse.Aux[L, R]
): Aux[A, L] = instance(l => g.from(l.reverse))
}
def toCaseClass[A](implicit tcc: ToCaseClass[A]): tcc.L => A = tcc.toCaseClass
toCaseClass[Result].apply(b :: a :: HNil)
Hiding several implicits with a type class: How to wrap a method having implicits with another method in Scala?
You could find an answer to your question in Type Astronaut:
https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:ops:migration (6.3 Case study: case class migrations)
Notice that IceCreamV1("Sundae", 1, true).migrateTo[IceCreamV2a] takes a single type parameter.
Your code with GraphOps doesn't work for several reasons.
Firstly, shapeless.Lazy is not just a wrapper. It's a macro-based type class to handle "diverging implicit expansion" (in Scala 2.13 there are by-name => implicits for that, although they are not equivalent to Lazy). You should use Lazy when you understand why you need it.
Secondly, you seem to define some implicit conversion (implicit view, Mat => A) but resolution of implicit conversions is trickier than resolution of other implicits (1 2 3 4 5).
Thirdly, you seem to assume that when you define
implicit def foo: Foo = ???
def useImplicitFoo(implicit foo1: Foo) = ???
foo1 is foo. But generally this is not true. foo is defined in current scope and foo1 will be resolved in the scope of useImplicitFoo call site:
Setting abstract type based on typeclass
When doing implicit resolution with type parameters, why does val placement matter? (difference between implicit x: X and implicitly[X])
So implicit createConverter is just not in scope when you call toCaseClass.
Fixed version of your code is
trait RunnableGraph[Mat]{
def mapMaterializedValue[A](a: Mat => A): RunnableGraph[A]
}
case class Wrapper[A, B](value: A => B)
implicit class GraphOps[Mat <: HList](g: RunnableGraph[Mat]) {
val ops = this
implicit def createConverter[A, RL <: HList](implicit
r: Reverse.Aux[Mat, RL],
gen: Generic.Aux[A, RL],
): Wrapper[Mat, A] =
Wrapper { l =>
val x: RL = l.reverse
val y: A = gen.from(x)
gen.from(l.reverse)
}
def toCaseClass[A](implicit convert: Wrapper[Mat, A]): RunnableGraph[A] = {
g.mapMaterializedValue(convert.value)
}
}
val g: RunnableGraph[B :: A :: HNil] = ???
val ops = g.ops
import ops._
g.toCaseClass[Result]
Try
import akka.stream.scaladsl.RunnableGraph
import shapeless.{::, Generic, HList, HNil}
import shapeless.ops.hlist.Reverse
implicit class GraphOps[Mat <: HList, R <: HList](g: RunnableGraph[Mat]) {
def toCaseClass[A](implicit
r: Reverse.Aux[Mat, R],
gen: Generic.Aux[A, R]
): RunnableGraph[A] = g.mapMaterializedValue(l => gen.from(l.reverse))
}
case class Result(one: String, two: Int)
val g: RunnableGraph[Int :: String :: HNil] = ???
g.toCaseClass[Result]
I am new to shapeless (and still low level in the learning curve of scala...) and i have some hard time with shapeless
import shapeless._
case class FooBar[T](foo: String, bar: T)
val hl = 0 :: FooBar("A", "one") :: FooBar("B", 1) :: "0" :: FooBar("C", "two") :: HNil
val l = hl.filter[FooBar[String]].toList
println(l) //List(FooBar(A,one), FooBar(C,two))
It works fine
Next step, i want to put that in function, something like
def filter[T](hl: HList): List[FooBar[T]] = ???
so i can simplify calling to
filter[String](hl)
filter[Int](hl)
naively i tested
def filter[T](hl: HList): List[FooBar[T]] = {
hl.filter[FooBar[T]].toList
}
which give
could not find implicit value for parameter partition: shapeless.ops.hlist.Partition[shapeless.HList,FooBar[T]]
after some tries playing with implicit, i still have not found the correct way to do that
Do you have any idea ?
Thank you !
If you lack some implicits then in your method you should suppose they are provided. Saying that an argument of the method is of type just HList (and not some specific L <: HList) is too rough.
Since probably you would like to specify T and not specify L (expecting that L will be inferred) try a type class + extension method
import shapeless._
import shapeless.ops.hlist.{Partition, ToTraversable}
case class FooBar[T](foo: String, bar: T)
val hl = 0 :: FooBar("A", "one") :: FooBar("B", 1) :: "0" :: FooBar("C", "two") :: HNil
trait FilterFooBar[L <: HList, T] {
def apply(l: L): List[FooBar[T]]
}
object FilterFooBar {
implicit def mkFilterFooBar[L <: HList, T, Prefix <: HList, Suffix <: HList](implicit
partition: Partition.Aux[L, FooBar[T], Prefix, Suffix],
toTraversable: ToTraversable.Aux[Prefix, List, FooBar[T]]
): FilterFooBar[L, T] = _.filter.toList
}
implicit class FilterFooBarOp[L <: HList](l: L) {
def filterFooBar[T](implicit filterFooBarInstance: FilterFooBar[L, T]): List[FooBar[T]] =
filterFooBarInstance(l)
}
println(hl.filterFooBar[String]) // List(FooBar(A,one), FooBar(C,two))
println(hl.filterFooBar[Int]) // List(FooBar(B,1))
Given traits for ProblemParser and Solver:
trait ProblemParser[Problem] {
def parse(description: String): Option[Problem]
}
trait Solver[Problem,Solution] {
def solve(problem: Problem): Option[Solution]
}
and HLists of parsers and solvers, I'm trying to apply all (type-appropriate) solvers to all the different Problem types that result from successful parses.
I can see how to obtain a HList of Problem Options with ~> :
object parseFn extends (ProblemParser ~> Option) {
def apply[P](x: ProblemParser[P]): Option[P] = x.parse(input)
}
Q. Given a HList of parsers of different Problem types, how do I then map the solvers over the list of parsed problems?
Presumably because Solver takes two type parameters this requires Poly1 rather than ~>?
Your question was a little concise so I split it in a couple of parts to my understanding :
The ProblemParser type class which can parse a description to a Problem.
String => Option[Problem]
A way to parse a list of descriptions to an HList of Problems using ProblemParser.
eg List[String] => Option[ProblemA] :: Option[ProblemB] :: HNil
The type class Solver which can give a Solution for a Problem.
Problem => Option[Solution]
Solving the Problems from step 2 using Solver.
eg Option[ProblemA] :: Option[ProblemB] :: HNil => Option[SolutionA] :: Option[SolutionB] :: HNil
We start with defining two simple problems, getting the sum or the maximum of a pair of integers :
case class Sum(a: Int, b: Int)
case class Max(a: Int, b: Int)
Now, we create the ProblemParser type class with two instances for our two problems :
import scala.util.Try
trait ProblemParser[Problem] extends Serializable {
def parse(description: String): Option[Problem]
}
object ProblemParser {
def apply[A](implicit pp: ProblemParser[A]): ProblemParser[A] = pp
def fromFunction[A](f: String => Option[A]): ProblemParser[A] =
new ProblemParser[A] { def parse(s: String): Option[A] = f(s) }
def intPairParser[A](f: (Int, Int) => A): ProblemParser[A] =
fromFunction { s =>
s.split(",") match {
case Array(l, r) =>
for {
ll <- Try(l.toInt).toOption
rr <- Try(r.toInt).toOption
} yield f(ll, rr)
case _ => None
}
}
implicit val sumParser: ProblemParser[Sum] = intPairParser(Sum.apply)
implicit val maxParser: ProblemParser[Max] = intPairParser(Max.apply)
}
Parsing the descriptions to an HList of Problems is similar to my answer in a different question :
import shapeless._
import scala.collection.GenTraversable
trait FromTraversableParsed[L <: HList] extends Serializable {
type Out <: HList
def apply(l: GenTraversable[String]): Out
}
object FromTraversableParsed {
def apply[L <: HList]
(implicit from: FromTraversableParsed[L]): Aux[L, from.Out] = from
type Aux[L <: HList, Out0 <: HList] = FromTraversableParsed[L] { type Out = Out0 }
implicit val hnilFromTraversableParsed: Aux[HNil, HNil] =
new FromTraversableParsed[HNil] {
type Out = HNil
def apply(l: GenTraversable[String]): Out = HNil
}
implicit def hlistFromTraversableParsed[H, T <: HList, OutT <: HList](implicit
ftpT: FromTraversableParsed.Aux[T, OutT],
parseH: ProblemParser[H]
): Aux[H :: T, Option[H] :: OutT] =
new FromTraversableParsed[H :: T] {
type Out = Option[H] :: OutT
def apply(l: GenTraversable[String]): Out =
(if(l.isEmpty) None else parseH.parse(l.head)) :: ftpT(l.tail)
}
}
We can now parse some descriptions :
val parse = FromTraversableParsed[Max :: Sum :: HNil]
parse(List("1,2", "1,2")) // Some(Max(1,2)) :: Some(Sum(1,2)) :: HNil
On to the Solver type class (I made Solution a dependent type) :
trait Solver[Problem] extends Serializable {
type Solution
def solve(problem: Problem): Option[Solution]
}
object Solver {
def apply[Problem]
(implicit solver: Solver[Problem]): Aux[Problem, solver.Solution] = solver
type Aux[Problem, Solution0] = Solver[Problem] { type Solution = Solution0}
implicit val solveMax: Aux[Max, Int] =
new Solver[Max] {
type Solution = Int
def solve(max: Max) = Some(math.max(max.a, max.b))
}
implicit val solveSum: Aux[Sum, Int] =
new Solver[Sum] {
type Solution = Int
def solve(sum: Sum) = Some(sum.a + sum.b)
}
}
With a HList of Problems and Solver instances for these problems, we should be able to map over our HList and use the correct Solvers to solve the Problems :
object solve extends Poly1 {
implicit def apply[Problem, Solution](
implicit solver: Solver.Aux[Problem, Solution]
): Case.Aux[Option[Problem], Option[Solution]] =
at[Option[Problem]](_.flatMap(solver.solve))
}
import shapeless.ops.hlist.Mapper
def parseAndSolve[Problems <: HList] =
new PartiallyAppliedParseAndSolve[Problems]
class PartiallyAppliedParseAndSolve[Problems <: HList] {
def apply[OP <: HList](descriptions: List[String])(implicit
ftp: FromTraversableParsed.Aux[Problems, OP],
mapper: Mapper[solve.type, OP]
): mapper.Out = mapper(ftp(descriptions))
}
With all this machinery in place we can now parse a list of descriptions and solve the parsed problems :
parseAndSolve[Max :: Sum :: HNil](List("1,2", "1,2"))
// Option[Int] :: Option[Int] :: HNil = Some(2) :: Some(3) :: HNil
I have the following problem, I want to map items of an HList to another HList but Strings in the source HList should only be converted to URL if the "target" type is URL.
val name = "Stackoverflow"
val url = "https://stackoverflow.com/q"
val list = name :: url :: HNil
val mapped: String :: URL :: HNil = list.map(???)
As far as my research took me is that all the Poly stuff only cares about the input type but not about the output type. So are there ways to archive my goal ?
I don't think you're going to get exactly what you want, since Scala's implicit resolution happens before type inference (but who knows—people do things that surprise me in Scala all the time).
(Side note: the CanBuildFrom / breakOut pattern supports something similar to what you're asking for, but I don't see a way to make it work in this situation, since the source type does constrain what instances are available.)
There is a pretty standard workaround for this kind of situation, though, involving the use of a helper class to approximate partial application of type parameters. Suppose you've got a fairly straightforward type class that captures your conversion logic:
import java.net.URL
import shapeless._
trait Convert[I <: HList, O <: HList] { def apply(i: I): O }
object Convert extends LowPriorityConvertInstances {
implicit val convertHNil: Convert[HNil, HNil] = new Convert[HNil, HNil] {
def apply(i: HNil): HNil = i
}
implicit def convertHConsURL[T <: HList, TO <: HList](implicit
c: Convert[T, TO]
): Convert[String :: T, URL :: TO] = new Convert[String :: T, URL :: TO] {
def apply(i: String :: T): URL :: TO = new URL(i.head) :: c(i.tail)
}
}
sealed class LowPriorityConvertInstances {
implicit def convertHCons[H, T <: HList, TO <: HList](implicit
c: Convert[T, TO]
): Convert[H :: T, H :: TO] = new Convert[H :: T, H :: TO] {
def apply(i: H :: T): H :: TO = i.head :: c(i.tail)
}
}
Now you might try something like this:
def convert[I <: HList, O <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
But there are two problems here. The first is that if you let the type parameters be inferred, you'll always get a conversion that turns every string into a URL. You can override this behavior by explicitly providing both type parameters, but ugh.
We can (kind of) improve this situation with a helper class:
class PartiallyAppliedConvert[O <: HList] {
def apply[I <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
}
def convert[O <: HList]: PartiallyAppliedConvert[O] =
new PartiallyAppliedConvert[O]
Now you can write the following:
scala> val mapped = convert[String :: URL :: HNil](list)
mapped: shapeless.::[String,shapeless.::[java.net.URL,shapeless.HNil]] = Stackoverflow :: https://stackoverflow.com/q :: HNil
It's not exactly what you asked for, but it's pretty close, in that the only type we have to specify explicitly is the desired target type.
My initial code:
sealed trait Adder[L <: HList, U] extends DepFn2[L, Vector[U]]
object Adder {
def apply[L <: HList, U: Ordering](implicit adder: Adder[L, U]): Aux[L, U, adder.Out] = adder
type Aux[L <: HList, U, Out0] = Adder[L, U] { type Out = Out0 }
implicit def found[T <: HList, U: Ordering]: Aux[Vector[U] :: T, U, Vector[U] :: T] =
new Adder[Vector[U] :: T, U] {
type Out = Vector[U] :: T
def apply(l: Vector[U] :: T, collection: Vector[U]): Out = {
(l.head ++ collection).sorted :: l.tail
}
}
implicit def notFound[H, T <: HList, U: Ordering, OutT <: HList](implicit ut: Aux[T, U, OutT]): Aux[H :: T, U, H :: OutT] =
new Adder[H :: T, U] {
type Out = H :: OutT
def apply(l: H :: T, collection: Vector[U]): Out = {
val outT = ut(l.tail, collection)
l.head :: outT
}
}
implicit def empty[U: Ordering]: Aux[HNil, U, Vector[U] :: HNil] =
new Adder[HNil, U] {
type Out = Vector[U] :: HNil
def apply(l: HNil, collection: Vector[U]): Out = collection :: HNil
}
}
I found a bug where things that don't have the context bound
Ordering, the type is passed via notFound instead of found,
which is in hinsight not suprising. I tried to fix the bug by adding
another implicit which should trigger when there is no Ordering:
implicit def foundNoOrdering[T <: HList, U]: Aux[Vector[U] :: T, U, Vector[U] :: T] =
new Adder[Vector[U] :: T, U] {
type Out = Vector[U] :: T
def apply(l: Vector[U] :: T, collection: Vector[U]): Out = {
l.head ++ collection :: l.tail
}
}
However, this results in an ambiguous implicit between the
foundNoOrdering and found. How can I have different code paths
dependent on if there is an Ordering or not?
The standard trick is to reduce the priority by putting the implicit in an ancestor trait
object Adder extends LowPriorityAdderImplicits {
implicit def found...
}
trait LowPriorityAdderImplicits {
implicit def foundNoOrdering....
}
You will find a few of those in the standard library. LowPriorityImplicits seems to be customary in the name.
In the specification:
SLS §7.2 Implicit parameters
If there are several eligible arguments which match the implicit
parameter’s type, a most specific one will be chosen using the rules
of static overloading resolution (§6.26.3)
SLS §6.26.3 : The relevant bit is too long to cite in full, but you have something about
A class or object C is derived from a class or object D if one of the
following holds:
• C is a subclass of D, or
• C is a companion object of a class derived from D, or
• D is a companion object of a class from which C is derived.
and there being derived making it more specific and getting priority in resolution. I believe that one was made just for implicit.