我昨晚尝试做一些接受和调用泛型函数的东西(即该类型在调用站点已知,但可能因调用站点而异,因此定义应该是跨arities的通用)。
例如,假设我有一个函数f: (A, B, C, ...) => Z
。 (实际上有很多这样的f
,我事先并不知道,所以我无法修复A, B, C, ..., Z
的类型和数量。)
我正在尝试实现以下目标。
如何使用f
的实例通常调用(A, B, C, ...)
?如果提前知道f
的签名,那么我可以做一些涉及Function.tupled f
或同等的事情。
如何使用与object
相同的签名定义另一个函数或方法(例如,某些apply
的{{1}}方法)?也就是说,如何定义f
g
类型检查当且仅当g(a, b, c, ...)
类型检查?我正在研究Shapeless的f(a, b, c, ...)
。从我到目前为止所知,HList
至少解决了“代表任意arity args列表”的问题,而且,Shapeless将解决与元组问题的转换。但是,我仍然不确定我是否理解这与通用arity的功能如何相符(如果有的话)。
如何定义具有HList
相关类型签名的其他函数或方法?现在想到的最大例子是一些f
。
我记得前一段时间在Shapeless上观看过一次会议。虽然演示者没有明确地展示这些内容,但他们所做的演示(围绕抽象/泛化元组与h: (A, B, C, ...) => SomeErrorThing[Z] \/ Z
的各种技巧)会让我相信使用相同的工具可以实现与上述类似的东西。
提前致谢!
答案 0 :(得分:11)
是的,Shapeless绝对可以帮到你。假设我们想要使用任意arity的函数并将其转换为同一个arity的函数,但返回类型包含在Option
中(我认为这将触及你问题的所有三个点)。 / p>
为了简单起见,我只想说Option
始终是Some
。这需要相当密集的四行:
import shapeless._, ops.function._
def wrap[F, I <: HList, O](f: F)(implicit
ftp: FnToProduct.Aux[F, I => O],
ffp: FnFromProduct[I => Option[O]]
): ffp.Out = ffp(i => Some(ftp(f)(i)))
我们可以证明它有效:
scala> wrap((i: Int) => i + 1)
res0: Int => Option[Int] = <function1>
scala> wrap((i: Int, s: String, t: String) => (s * i) + t)
res1: (Int, String, String) => Option[String] = <function3>
scala> res1(3, "foo", "bar")
res2: Option[String] = Some(foofoofoobar)
请注意适当的静态返回类型。现在如何它的工作原理:
FnToProduct
类型类提供的证据表明某些类型F
是FunctionN
(对于某些N
),可以从某些HList
转换为函数到原始输出类型。 HList
函数(确切地说是Function1
)是实例的Out
类型成员,或FnToProduct.Aux
帮助器的第二个类型参数。
FnFromProduct
反过来证明某些F
是从Function1
到某种输出类型的HList
,可以将其转换为某个arity的函数那个输出类型。
在我们的wrap
方法中,我们使用FnToProduct.Aux
约束Out
FnToProduct
F
实例的HList
,以便我们可以参考我们的O
实例类型中的FnFromProduct
参数列表和struct
结果类型。然后,实现非常简单 - 我们只是在适当的位置应用实例。
这可能看起来都非常复杂,但是一旦你在Scala中使用这种通用编程一段时间它变得或多或少都很直观,我们当然乐意回答有关你使用的更具体的问题情况下。