Krever Krever - 1 month ago 6
Scala Question

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.

Answer

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.