scalashapelesstype-constructor

FnToProduct not working with a Type Constructor


Why does this works as expected:

def method1[L <: HList, M <: HList, F, R](list: L)(builder: F)
           (implicit ev: Comapped.Aux[L, Seq, M],
           fntp: FnToProduct.Aux[F, M => R]) = println("asd")

method1(Seq("asd") :: HNil) { (s: String) =>
  6
}

But this does not?

def method2[L <: HList, M <: HList, F, R](list: L)(builder: F)
           (implicit ev: Comapped.Aux[L, Seq, M],
           fntp: FnToProduct.Aux[F, M => Seq[R]]) = println("asd")

method2(Seq("asd") :: HNil) { (s: String) =>
  Seq(6)
}

It seems that adding a type constructor in the return type for the FnToProduct.Aux breaks it.


Solution

  • The issue is that when the compiler is trying to infer the type parameters for method1, it's going to find L and M and F with no problems, but then when it gets to R it's not going to be able to tell that the second parameter of the FnToProduct.Aux has the right shape to line up with M => Seq[R].

    Off the top of my head I don't have a good explanation of why this happens in the second case and not the first, but it's a limitation you'll run into fairly often, and there are a couple of workarounds.

    The first would be to leave the Aux off of the FnToProduct altogether. Depending on what your actual method is doing, this may be just fine, but if you need to refer to R explicitly it's not going to work.

    The second workaround would be to let the return type of that second type parameter be inferred, and then ask for evidence that it's a Seq:

    import shapeless._, ops.function.FnToProduct, ops.hlist.Comapped
    
    def method2[L <: HList, M <: HList, F, S, R](list: L)(builder: F)
      (implicit
        ev: Comapped.Aux[L, Seq, M],
        fntp: FnToProduct.Aux[F, M => S],
        s: S <:< Seq[R]
      ) = println("asd")
    
    method2(Seq("asd") :: HNil) { (s: String) => Seq(6) }
    

    It's a little extra syntactic noise, but it works.