You actually don't really need or want a polymorphic function here—you can get what you're looking for from a few type classes that Shapeless provides out of the box. It looks a little odd, but it's not really all that complicated (note that I'm using Shapeless 2.0—you could do this in 1.2.4, but it'd be messier):
import shapeless._, ops.tuple.{ ConstMapper, ToList, ZipApply }
import shapeless.syntax.std.tuple._
def execute[F <: Product, S, O](funs: F)(implicit
cm: ConstMapper.Aux[F, Session, S],
za: ZipApply.Aux[F, S, O],
tl: ToList[O, Record]
): O = db withSession { session: Session =>
funs.zipApply(funs.mapConst(session))
}
We're essentially just taking our session, making a new tuple by repeating it as many times as our input is long, zipping the input tuple with this new tuple of sessions, and then applying the first part of each zipped element to the second part. The ToList
part requires all elements of the resulting tuple to be subtypes of Record
.
For the sake of a complete working example, here are some simple demonstration definitions:
type Session = String
trait Record
case class RecordX(s: String) extends Record
case class RecordY(i: Int) extends Record
def x(s: Session) = RecordX(s)
def y(s: Session) = RecordY(s.size)
object db {
def withSession[T](f: Session => T) = f("foo")
}
And it works!
scala> execute((x _, y _))
res0: (RecordX, RecordY) = (RecordX(foo),RecordY(3))
We get a nice appropriately statically typed tuple as our result.