2
votes

Suppose I have container-marker

case class TypedString[T](value: String)

and partial function trick

abstract class PartFunc[Actual <: HList] {
    val poly: Poly

    def apply[L1 <: HList](l: L1)(implicit
                                  mapped: Mapped.Aux[Actual, TypedString, L1],
                                  mapper: Mapper[poly.type, L1]): L1 = l
}

Poly for Mapper

object f extends (TypedString ~>> String) {
    def apply[T](s : TypedString[T]) = s.value
}

and result method

def func[Actual <: HList] = new PartFunc[Actual] {
    val poly = f
}

Example of usage:

func[
    Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)

This code fails at compile time because compiler cannot find Mapped implicit parameter:

could not find implicit value for parameter mapped: 
    shapeless.ops.hlist.Mapped[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],nottogether.MapperTest.TypedString]{type Out = shapeless.::[nottogether.MapperTest.TypedString[Int],shapeless.::[nottogether.MapperTest.TypedString[String],shapeless.HNil]]}
        ](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)

But if we remove Mapper implicit parameter from PartFunc.apply(...) signature all works fine. So I have no idea why and how Mapper influence on Mapped.

1

1 Answers

3
votes

The compiler complains about Mapped while the actual issue is with Mapper. I am not sure why, but there seems to be going something wrong with getting a Mapped for the singleton type poly.type when poly is an abstract value or a constructor argument of PartFunc.

A solution would be to make poly a P <: Poly and passing the singleton type along with Actual when we create the PartFunc :

import shapeless._
import ops.hlist.{Mapped, Mapper}
import poly.~>>

abstract class PartFunc[Actual <: HList, P <: Poly] {
  val poly: P
  def apply[L1 <: HList](l: L1)(implicit
    mapped: Mapped.Aux[Actual, TypedString, L1],
    mapper: Mapper[P, L1]
  ): mapper.Out = mapper(l)
}

def func[Actual <: HList] = new PartFunc[Actual, f.type] { val poly = f }

Or with a regular class :

class PartFunc[Actual <: HList, P <: Poly](poly: P) {
  def apply[L1 <: HList](l: L1)(implicit
    mapped: Mapped.Aux[Actual, TypedString, L1],
    mapper: Mapper[P, L1]
  ): mapper.Out = mapper(l)
}

def func[Actual <: HList] = new PartFunc[Actual, f.type](f)

Notice that we now have to write mapper(l), because l map poly will still look for a Mapped[poly.type, L1].

We can now call func :

func[
  Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
// String :: String :: HNil = 42 :: hello :: HNil

I am sure that someone with some more in depth knowledge of the Scala type system could provide us with a clearer explanation and possibly a better solution for this problem.