使用裸类型参数创建无形多态函数

时间:2014-03-15 01:06:41

标签: scala polymorphism shapeless

如此处所述

Type-safe way to divide a tuple into multiple tuples

我有一个带有以下签名的方法

def execute[T <: Record](funs: Seq[(Session) => T]): Seq[T]

其中SessionSlick数据库会话;

这个方法的基本实现
def execute[T <: Record](funs: Seq[(Session) => T): Seq[T] = {
  db withSession { 
    session: Session => funs.map(fun => fun(session))
}}

(其中db是Slick Database),其他实现添加了日志记录,缓存,多线程等功能。特别是,多线程实现使用funs.grouped(ceil(funs.size / threadCount)).map(funs => Future {})在几个线程之间划分函数。

我想创建一个接受函数元组的方法版本,以便我可以返回不同类型的值 - 如上面链接的问题所述,我不知道一个好的分割方法将一个元组组合成较小的元组,然后重新组合多线程案例的结果,但该问题的答案是使用Shapeless库的HList - 但我不清楚为什么我可以创建(Session) => T函数的多态变体,问题是我所见过的所有多态函数的例子都使用包裹类型参数,例如(Set ~> Option)每个都包含多态T,但我正在尝试创建(Session ~> T)函数,Session不变,多态T不是{\ n}包裹在SetOption等等。由于没有足够的无形体验,我无疑以错误的方式看待这个问题。

如何使用Shapeless创建def execute(funs: Seq[(Session) => T]): Seq[T]函数的多态版本?

1 个答案:

答案 0 :(得分:3)

你实际上并不真正需要或想要一个多态函数 - 你可以从Shapeless提供的几个类型类中获得你正在寻找的东西。它看起来有点奇怪,但它并不是那么复杂(请注意我使用的是Shapeless 2.0 - 你可以在1.2.4中做到这一点,但它会变得更加混乱):

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))
}

我们基本上只是参加我们的会话,通过重复多次来创建一个新元组,因为我们的输入很长,用​​这个新的会话元组压缩输入元组,然后应用每个压缩的第一部分元素到第二部分。 ToList部分要求生成的元组的所有元素都是Record的子类型。

为了完整的工作示例,这里有一些简单的演示定义:

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")
}

它有效!

scala> execute((x _, y _))
res0: (RecordX, RecordY) = (RecordX(foo),RecordY(3))

我们得到一个很好的适当静态类型的元组作为我们的结果。