Scala Function.tupled 和 Function.untupled 等价于变量元数,或者,用元组调用变量元数函数
Scala Function.tupled and Function.untupled equivalent for variable arity, or, calling variable arity function with tuple
昨晚我试图做一些关于接受和调用通用函数的事情(即类型在调用站点是已知的,但可能在调用站点之间有所不同,因此定义应该是通用的)。
例如,假设我有一个函数f: (A, B, C, ...) => Z
。 (这样的f
其实有很多,我事先不知道,所以无法确定A, B, C, ..., Z
的类型和数量。)
我正在努力实现以下目标。
如何使用 (A, B, C, ...)
的实例调用 f
?如果事先知道 f
的签名,那么我可以做一些涉及 Function.tupled f
或等效的事情。
如何定义与f
具有相同签名的另一个函数或方法(例如,某些object
的apply
方法)?也就是说,我如何定义一个 g
当且仅当 f(a, b, c, ...)
类型检查时 g(a, b, c, ...)
类型检查?为此,我正在研究 Shapeless 的 HList
。到目前为止,据我所知,HList
至少解决了 "representing an arbitrary arity args list" 问题,而且,Shapeless 将解决与元组之间的转换问题。但是,我仍然不确定我是否理解这将如何适应通用 arity 函数(如果有的话)。
如何定义另一个具有与 f
相关的类型签名的函数或方法?现在想到的最大的例子就是一些h: (A, B, C, ...) => SomeErrorThing[Z] \/ Z
.
我记得前段时间看过一个关于 Shapeless 的会议演讲。虽然演示者没有明确展示这些东西,但他们展示的东西(围绕 abstracting/genericizing 元组与 HList
s 的各种技术)会让我相信使用相同的工具可以实现与上述类似的事情。
提前致谢!
是的,Shapeless 绝对可以帮助您。例如,假设我们想要采用任意数量的函数并将其转换为相同数量的函数,但 return 类型包含在 Option
中(我认为这会触及你的所有三个点问题)。
为简单起见,我只说 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)
注意适当的静态 return 类型。现在如何工作:
FnToProduct
类型 class 提供证据表明某些类型 F
是可以转换为函数的 FunctionN
(对于某些 N
)从一些 HList
到原始输出类型。 HList
函数(准确地说是Function1
)是实例的Out
类型成员,或者是FnToProduct.Aux
助手的第二个类型参数。
FnFromProduct
做相反的事情——它证明一些 F
是一个 Function1
从 HList
到一些可以转换成一些函数的输出类型该输出类型的 arity。
在我们的 wrap
方法中,我们使用 FnToProduct.Aux
来约束 F
的 FnToProduct
实例的 Out
,这样我们就可以在我们的 FnFromProduct
实例的类型中参考 HList
参数列表和 O
结果类型。实现非常简单——我们只需在适当的地方应用实例。
这一切看起来可能非常复杂,但是一旦您在 Scala 中使用这种泛型编程一段时间后,它就会或多或少地变得直观,我们当然很乐意回答更具体的问题你的用例。
昨晚我试图做一些关于接受和调用通用函数的事情(即类型在调用站点是已知的,但可能在调用站点之间有所不同,因此定义应该是通用的)。
例如,假设我有一个函数f: (A, B, C, ...) => Z
。 (这样的f
其实有很多,我事先不知道,所以无法确定A, B, C, ..., Z
的类型和数量。)
我正在努力实现以下目标。
如何使用
(A, B, C, ...)
的实例调用f
?如果事先知道f
的签名,那么我可以做一些涉及Function.tupled f
或等效的事情。如何定义与
f
具有相同签名的另一个函数或方法(例如,某些object
的apply
方法)?也就是说,我如何定义一个g
当且仅当f(a, b, c, ...)
类型检查时g(a, b, c, ...)
类型检查?为此,我正在研究 Shapeless 的HList
。到目前为止,据我所知,HList
至少解决了 "representing an arbitrary arity args list" 问题,而且,Shapeless 将解决与元组之间的转换问题。但是,我仍然不确定我是否理解这将如何适应通用 arity 函数(如果有的话)。如何定义另一个具有与
f
相关的类型签名的函数或方法?现在想到的最大的例子就是一些h: (A, B, C, ...) => SomeErrorThing[Z] \/ Z
.
我记得前段时间看过一个关于 Shapeless 的会议演讲。虽然演示者没有明确展示这些东西,但他们展示的东西(围绕 abstracting/genericizing 元组与 HList
s 的各种技术)会让我相信使用相同的工具可以实现与上述类似的事情。
提前致谢!
是的,Shapeless 绝对可以帮助您。例如,假设我们想要采用任意数量的函数并将其转换为相同数量的函数,但 return 类型包含在 Option
中(我认为这会触及你的所有三个点问题)。
为简单起见,我只说 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)
注意适当的静态 return 类型。现在如何工作:
FnToProduct
类型 class 提供证据表明某些类型 F
是可以转换为函数的 FunctionN
(对于某些 N
)从一些 HList
到原始输出类型。 HList
函数(准确地说是Function1
)是实例的Out
类型成员,或者是FnToProduct.Aux
助手的第二个类型参数。
FnFromProduct
做相反的事情——它证明一些 F
是一个 Function1
从 HList
到一些可以转换成一些函数的输出类型该输出类型的 arity。
在我们的 wrap
方法中,我们使用 FnToProduct.Aux
来约束 F
的 FnToProduct
实例的 Out
,这样我们就可以在我们的 FnFromProduct
实例的类型中参考 HList
参数列表和 O
结果类型。实现非常简单——我们只需在适当的地方应用实例。
这一切看起来可能非常复杂,但是一旦您在 Scala 中使用这种泛型编程一段时间后,它就会或多或少地变得直观,我们当然很乐意回答更具体的问题你的用例。