【问题标题】:Creating a shapeless polymorphic function with a naked type param使用裸类型参数创建无形多态函数
【发布时间】:2014-03-28 03:28:15
【问题描述】:

如此处所述

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 =&gt; Future {}) 将功能划分为几个线程。

我想创建一个接受函数元组的方法版本,以便我可以返回不同类型的值 - 如上面链接的问题中所述,我不知道拆分的好方法元组成更小的元组,然后重新组合多线程案例的结果,但该问题的答案是使用 Shapeless 库的 HLists - 但是我不清楚如何创建(Session) =&gt; T 函数,问题是我见过的所有多态函数示例都使用包装类型参数,例如(Set ~&gt; Option) 每个都包含一个多态 T,但我正在尝试创建一个 (Session ~&gt; T) 函数,其中 Session 是不变的,而多态 T 不包含在 SetOption 等中。由于没有足够的 Shapeless 经验,我毫无疑问地以错误的方式看待这个问题。

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

【问题讨论】:

  • 这里删除ToListexecute的返回类型是什么?还有其他方法可以强制执行约束,但我不确定为什么这个不起作用。
  • @TravisBrown 我的错,我忘了数据库返回 Option[Record];如果我将参数更改为 ToList[0, Option[Record]] 那么它工作得很好

标签: scala polymorphism shapeless


【解决方案1】:

您实际上并不真正需要或想要一个多态函数——您可以从 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))

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

【讨论】:

  • 谢谢,如果我删除 ToList 参数,您的示例将有效;包含参数后,我得到编译器错误“找不到参数 tl 的隐式值:shapeless.ops.tuple.ToList[O,Record]”;我正在复制你的代码,除了我已经用实际的 Record 实例和数据库调用替换了你的存根 RecordwithSession;我正在用我使用的代码编辑我的问题。
  • 作为旁注,正如 Cody Allen 指出的 here,这看起来很像通过 reader monad 对 HList 进行排序。 Typelevel 项目does providesequence for HLists,但它有点棘手,仍然需要一些工作——让它在这里工作并非易事。您可以查看my answer here 了解有关HLists 测序的更多详细信息,但现在您可能最好使用这里的方法。
  • 作为旁注的旁注,如果您导入 scalaz.std.function._ 您将获得使用原始函数执行此操作所需的类型类实例,而无需将它们包装在 Reader 中。
猜你喜欢
  • 2016-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
相关资源
最近更新 更多