使用无形状我们可以使用他们的文档中描述的配方来抽象arity。
def applyProduct[P <: Product, F, L <: HList, R](p: P)(f: F)(implicit gen: Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) =
f.toProduct(gen.to(p))
val add = (x: Int, y: Int) => x + y
def wrapper[F](f: F) {
applyProduct(1, 2)(f)
}
wrapper(add)
但是,我无法包装此设施,例如:
find implicit value for parameter fp: shapeless.ops.function.FnToProduct.Aux[(A, A) => A,L => R]
编译器辩称它不能case class Node[P <: Product, F, L <: HList, R](f: F)(implicit gen:Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) {
def evaluate(p: P) = {
f.toProduct(gen.to(p))
}
}
意味着它无法将K-ary函数转换为相同参数类型的K大小的HList函数。
我怎样才能让它发挥作用?
修改
好的,现在想象一下我在某个时间知道这个功能,但我稍后会知道这些论点,所以我想推迟评估
val add = (x: Int, y: Int) => x + y
val n = Node(add)
//later
n.evaluate(1,2,3)
编译器不允许我调用:
${javascript:
我不能用部分应用来解决这个问题吗?
答案 0 :(得分:3)
就这么简单:
def wrapper[R](f: (Int, Int) => R) = {
applyProduct(1, 2)(f)
}
您已经通过在包装器中传递P
来定义(Int, Int)
类型(作为(1,2)
),因此对F
进行抽象没有任何意义 - 仅限R
你可以抽象的东西是applyProduct
(结果类型)
说明:
wrapper
内的{p> F
对FnToProduct.Aux[F, ...]
一无所知。因此,为了隐式查找F
scala编译器需要知道有关FnToProduct.Aux
是什么的更多信息,导致在其上隐式定义(A, A) => A
的“形状”为{{1} (在你的情况下是(Int, Int) => Int
),而不仅仅是F
,哪个编译器从错误信息中诚实地告诉你。
对编辑的回应:
@ applyProduct(_: Int, _: Int)(add)
res17: (Int, Int) => Int =
@ res17(1,2)
res18: Int = 3
为了避免类型归属,您可以使用类似Node
案例类的东西(使用案例类来避免new
不被认为是一种好的风格),但不引入类(只是从函数返回lambda)。但是,与Node
一样,如果不传递所有类型参数,Scala将无法进行正确的类型推断。
不幸的是,你不能在这里方便地使用currying(通过使f: F
成为第一个参数)因为“implicits”在没有传递所有类型参数的情况下无法解决。也许有办法破解它,但部分应用似乎最简单易懂。
P.S。然而,您可能会注意到,对于这种情况,此类部分应用程序等同于:
@ val ff = add _; val f = ff()
ff: () => (Int, Int) => Int =
f: (Int, Int) => Int = ammonite.$sess.cmd9$$$Lambda$1978/545666041@6e7e60bb
@ f(1,2)
res34: Int = 3
拥有一个带有两个(或List of)参数(reducer)的函数更有意义,但是转换为任意arity的函数,比如def abstract[...](f: (A, A) => A)(p: P): A
。这将是对arity的更真实的抽象。