2
votes

Given a type dependent Converter type class which can convert a String into an Integer:

trait Converter[From] {
  type To
  def to(from: From): To
}

object Converter {
  type Aux[From, Out0] = Converter[From] { type To = Out0 }

  implicit val forInteger = new Converter[Integer] {
    type To = String
    def to(value: Integer) = ???
  }
}

// this works
val single = the[Converter[Integer]]
implicitly[single.To =:= String]
val singleAux = the[Converter.Aux[Integer, String]]

I would like it to work for HLists, e.g. Integer :: HNil. In theory, all I need is implicits for HNil and HList:

implicit val forHNil = new Converter[HNil] {
  type To = HNil
  def to(value: HNil) = HNil
}

implicit def forHList[Head, HeadConverted, Tail <: HList, TailConverted <: HList](
  implicit
  hConverter: Converter.Aux[Head, HeadConverted],
  tConverter: Converter.Aux[Tail, TailConverted]
  ): Converter[Head :: Tail] = new Converter[Head :: Tail] {
    type To = HeadConverted :: TailConvertedHList
    def to(values: Head :: Tail) = ???
  }

The above works fine for HNil:

val hnil = the[Converter[HNil]]
implicitly[hnil.To =:= HNil]
val hnilAux = the[Converter.Aux[HNil, HNil]]

But not for an HList. Interestingly, it does find an instance, but does not derive the resulting type

val hlist = the[Converter[Integer :: HNil]]
type expected = String :: HNil
implicitly[hlist.To =:= expected] //fails
val hlistAux = the[Converter.Aux[Integer :: HNil, String :: HNil]] // fails

I've set it up in a self contained project which has a few more (failed) attempts to debug the issue: https://github.com/mpollmeier/shapeless-dependent-type-typeclass-problem

1
Surely in the failing case the expected type is String :: HNil? Also the IsHList looks redundant ... put a <: HList on TailConverted instead. If you make those changes then the remaining problem is that the result type of forHList should be Aux[Head :: Tail, HeadConverted :: TailConverted].Miles Sabin
Ha, by explicitly declaring the (incomplete) return type I made the compiler forget the type - thank you so much! The expected type should indeed be String :: HNil. IsHList is obviously obsolete, I only introduced it for it's Aux type, hoping it could help the compiler figure our the correct type. However that didn't work anyway. I have updated the question, and will post an answer based on your comment, so that the next person can parse this easier. Hopefully this will get easier in the future with multiple implicit blocks (github.com/scala/scala/pull/5108).Michael Pollmeier

1 Answers

1
votes

The problem is that I explicitly stated the return type of forHList as Converter[Head :: Tail]. That return type is incomplete, and it's missing exactly the part that I needed.

Just changing the declared return type to Converter.Aux[Head :: Tail, HeadConverted :: TailConverted] (or even just leaving it out) fixes the problem.

All credit for this answer goes to Miles Sabin, he mentioned the problem in the above comment. Thank you!