Search code examples
scalashapeless

Omit intermediate types in function signature


I would like to perfom two maps over shapeless HList, I have folllowing code that works:

object doubleToInt extends Poly1 {
  implicit def caseDouble = at[Double](_.toInt)
}


object intToDouble extends Poly1 {
  implicit def caseInt = at[Int](_.toDouble)
}

def convert[InputType<:HList, ResultType<:HList, IntermediateType<:HList](input: InputType)(
  implicit mapper1: Mapper.Aux[doubleToInt.type , InputType, IntermediateType],
  mapper2: Mapper.Aux[intToDouble.type, IntermediateType, ResultType]
): ResultType = {
  input.map(doubleToInt).map(intToDouble)
}

convert(2.5 :: HNil)

as we can see the type of convert contains a type parameter IntermediateType that is completely irrelevant to the caller of this function. Is it posible to hide it somehow? It's a problem because I would like to call like this:

convert[SomeType, SomeType2, _](2.5 :: HNil)

but it doesn't compile because of unbound type parameter.


Solution

  • The normal solution when you want to infer only some type parameters of a method call is to split it into 2: the first fixes parameters you are interested in and returns a helper with apply method which is called to infer the rest. Applying this here, you'd get

    def convert[ResultType <: HList] = new ConvertTo[ResultType]
    
    class ConvertTo[ResultType <: HList] {
      def apply[InputType <: HList, IntermediateType <: HList](input: InputType)(implicit mapper1: Mapper.Aux[doubleToInt.type, InputType, IntermediateType],
      mapper2: Mapper.Aux[intToDouble.type, IntermediateType, ResultType]
    ) = input.map(doubleToInt).map(intToDouble)
    }
    

    Usage: convertTo[SomeType].apply(2.5 :: HNil). InputType is inferred from input and then the compiler finds the implicit mapper1 and infers IntermediateType.

    Or at least, ideally it would: due to the way Scala type inference works, it can't infer IntermediateType from mapper1 and then use it in mapper2, so I am not sure it'll find mapper2 correctly. In future Scala versions this should be fixed by def apply(implicit mapper1: ...)(implicit mapper2: ...), but the pull request enabling this wasn't accepted in time for 2.12.0. If this turns out to be a problem, you'll have to split ConvertTo.apply in 2 again.