Skip to content

implicit resolution error for Aux pattern with parametrized type #10528

@scalasolist

Description

@scalasolist

Description

The error shows itself when implicit resolution tried to find Aux pattern for a type that has parameter. The same case without Aux pattern or without type parameter compiles fine. I expect that more complicated example should also compile, but it fails.

Code

Error reproduction

The error could be reproduced with the following code:

trait Holder[A]
trait NilHolder[A] extends Holder[A]

trait Solve[A, H <: Holder[A]] {
  type Output <: Holder[A]
}
type SolveAux[A, H <: Holder[A], O <: Holder[A]] = Solve[A, H] {type Output = O}

implicit def nilSolve[A] = new Solve[A, NilHolder[A]] {
  override type Output = NilHolder[A]
}

trait WrapSolve[A, H <: Holder[A]] {
  type Output <: Holder[A]
}

implicit def wrapAux[A, H <: Holder[A], O <: Holder[A]](implicit one : SolveAux[A, H, O]) =
  new WrapSolve[A, H] {
    override type Output = O
  }

val wrapped = implicitly[WrapSolve[String, NilHolder[String]]]

Error message

OutsideImplicitTree.scala:30: error: could not find implicit value for parameter e: Parametrized.WrapSolve[String,Parametrized.NilHolder[String]]
  val wrapped = implicitly[WrapSolve[String, NilHolder[String]]]
                          ^
one error found

I tried to -Xlog-implicits and it showed quite useful info. nilSolve was successfully adopted as implicit argument, but typechecker suddenly failed to recognized its type:

OutsideImplicitTree.scala:30: nilSolve is not a valid implicit value for Parametrized.SolveAux[String,Parametrized.NilHolder[String],O] because:
type parameters weren't correctly instantiated outside of the implicit tree: inferred type arguments [this.Output] do not conform to method wrapAux's type parameter bounds [O <: Parametrized.Holder[A]]
  val wrapped = implicitly[WrapSolve[String, NilHolder[String]]]
                          ^

Workaround

If you chose to abandon Aux pattern compiler recognizes types immediately:

implicit def wrapNoAux[A, H <: Holder[A]](implicit one : Solve[A, H]) =
  new WrapSolve[A, H] {
    override type Output = one.Output
  }

When workaround does not work

This workaround works but is not universally applicable. For example, what If I need to perform some transformation twice?

trait TwoTimes[A, H <: Holder[A]] {
  type Output <: Holder[A]
}

implicit def twoTimes[A, H <: Holder[A], O <: Holder[A]](implicit
  one : SolveAux[A, H, O],
  two : Solve[A, O]
) = new TwoTimes[A, H] {
  override type Output = two.Output
}

Comparing to simple type

If the Holder type is not parametrized the entire thing works flawlessly:

trait Holder
trait NilHolder extends Holder

trait Solve[A, H <: Holder] {
  type Output <: Holder
}
type SolveAux[A, H <: Holder, O <: Holder] = Solve[A, H] {type Output = O}

implicit def nilSolve[A] = new Solve[A, NilHolder] {
  override type Output = NilHolder
}

trait WrapSolve[A, H <: Holder] {
  type Output <: Holder
}

implicit def wrapAux[A, H <: Holder, O <: Holder](implicit one : SolveAux[A, H, O]) =
  new WrapSolve[A, H] {
    override type Output = O
  }

val wrapped = implicitly[WrapSolve[String, NilHolder]]

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions